open-memex 0.3.0 → 0.4.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/AGENTS.md +20 -1
  2. package/README.md +117 -9
  3. package/README.zh-CN.md +103 -9
  4. package/dist/cli.js +583 -14
  5. package/dist/config.js +12 -0
  6. package/dist/distill-agents.js +55 -0
  7. package/dist/doctor.js +1 -1
  8. package/dist/export.js +157 -0
  9. package/dist/github.js +215 -0
  10. package/dist/index.js +6 -0
  11. package/dist/init.js +43 -18
  12. package/dist/mcp.js +119 -12
  13. package/dist/paths.js +31 -0
  14. package/dist/providers/git.js +142 -0
  15. package/dist/retrieve/inject.js +2 -2
  16. package/dist/retrieve/search.js +31 -6
  17. package/dist/review.js +437 -0
  18. package/dist/store/db.js +3 -2
  19. package/dist/store/lifecycle.js +28 -13
  20. package/dist/store/markdown.js +57 -14
  21. package/dist/store/sync.js +74 -6
  22. package/dist/submit.js +347 -0
  23. package/dist/tools/ops.js +171 -8
  24. package/docs/CURATOR.md +59 -0
  25. package/docs/USER-GUIDE.md +197 -0
  26. package/docs/USER-GUIDE.zh-CN.md +161 -0
  27. package/docs/V2-DESIGN.md +287 -13
  28. package/package.json +1 -1
  29. package/scripts/smoke-mcp.ts +2 -2
  30. package/src/cli.ts +608 -15
  31. package/src/config.ts +26 -0
  32. package/src/distill-agents.ts +70 -0
  33. package/src/doctor.ts +1 -1
  34. package/src/export.ts +206 -0
  35. package/src/github.ts +257 -0
  36. package/src/index.ts +6 -0
  37. package/src/init.ts +43 -17
  38. package/src/mcp.ts +166 -11
  39. package/src/paths.ts +32 -0
  40. package/src/providers/git.ts +191 -0
  41. package/src/retrieve/inject.ts +2 -2
  42. package/src/retrieve/search.ts +33 -6
  43. package/src/review.ts +504 -0
  44. package/src/store/db.ts +3 -2
  45. package/src/store/lifecycle.ts +30 -12
  46. package/src/store/markdown.ts +91 -14
  47. package/src/store/sync.ts +85 -4
  48. package/src/submit.ts +424 -0
  49. package/src/tools/ops.ts +203 -9
@@ -0,0 +1,161 @@
1
+ # OpenMemex 用户守则
2
+
3
+ > 本文档讲的是 open-memex 的**心智模型**:你的记忆住在哪里、怎么流动、谁能看到。
4
+ > in-repo 目录(§2、§6 的写路径)和 propose → promote → resolve 工作流(§5)
5
+ > 已在 `V2-dev-p2b` 分支实现;标有 **2B** 的功能属于 Phase 2B 待实现部分,
6
+ > 其余为 0.3.0 已有行为。
7
+
8
+ ## 一句话
9
+
10
+ open-memex 是本地优先的记忆层:**Markdown 是真相,SQLite 只是可重建的索引**。索引丢了可以重建,
11
+ Markdown 丢了才是真的丢了。
12
+
13
+ ## 1. 两种 Scope:personal 与 project
14
+
15
+ 每条记忆都属于一个 scope,决定它**能走多远**:
16
+
17
+ | | personal | project |
18
+ |---|---|---|
19
+ | 存什么 | 关于你的事实:偏好、习惯、跨项目的信息 | 关于项目的事:技术栈、约定、决策、踩过的坑 |
20
+ | 存哪里 | 只在你本机(见 §2) | 本机 + 可进团队共享(见 §2、§6) |
21
+ | 一句话 | **永不离开这台机器** | 可以晋升为团队知识 |
22
+
23
+ 关键词会自动路由(D18):「记住…/我觉得…/我喜欢…」(我)→ personal;
24
+ 「我们认为…/我们决定…/帮我们记住…」(我们)→ project。
25
+
26
+ ## 2. 两个家:appdata 与 repo 目录
27
+
28
+ | | appdata(书桌) | repo `.ai/open-memex/`(书架) |
29
+ |---|---|---|
30
+ | 位置 | Windows `%APPDATA%/open-memex`,Linux `~/.local/share/open-memex` | 项目根目录下,默认 `.ai/open-memex/`(D23,可配置 `memoryDir`) |
31
+ | 放什么 | `memories/personal/` 个人记忆;project scope **草稿箱(outbox)**;`index.db` 本地索引 | 已提交的 project 记忆(`proposed` 及以上) |
32
+ | 进 git 吗 | 不进 | 进,git 就是它的搬运工 |
33
+ | 索引呢 | `index.db` 可重建,**永远不入库** | 不存索引,用时从 Markdown 重建 |
34
+
35
+ 个人笔记本(personal)永远不上书架。这是铁律,不是配置项。
36
+
37
+ project 记忆先以**草稿**身份住在 appdata outbox——git 看不见、跟分支无关。
38
+ 只有你亲手点名的草稿,才会被 `open-memex submit` 移上书架(§5)。
39
+ 一个阶段只住一个地方:submit 成功后 outbox 原件消失;submit 之前,
40
+ repo 里什么都不知道。
41
+
42
+ ## 3. 首轮注入:8 条和 5 条是怎么选出来的
43
+
44
+ Agent 第一轮发言前,open-memex 会塞一个 `[OPEN-MEMEX]` 上下文块进去,省得每次先调一次搜索。
45
+ 默认注入 **project 8 条、personal 5 条**(`maxProjectMemories` / `maxProfileItems`,可调)。
46
+
47
+ 入选标准只有一个:**最近更新优先**(`ORDER BY updated_at DESC`),并过滤掉已撤回/归档、
48
+ 解析掉被替代的旧版本。不是"最重要的 N 条",而是"最近在折腾的 N 条"——
49
+ 你刚更新过的东西,最可能跟当前工作相关。
50
+
51
+ Token 账:每条压成一行(≤240 字符,约 60 token),8 条 ≈ 500 token 上限。
52
+ 超过这个数收益递减——长尾靠 `memory_search` 按需查,全塞进首轮反而淹没信号。
53
+
54
+ 调大:`open-memex config set maxProjectMemories 12`
55
+
56
+ ## 4. 后续轮次:什么时候会再搜记忆
57
+
58
+ 首轮注入只发生一次。之后**没有自动重搜机制**——MCP 是 request/response 模式,
59
+ server 不能主动推送,调不调 `memory_search` 全看 model 的判断。引导它的有两处:
60
+
61
+ - 注入块页脚:"Use the `memory_search` tool to look up more."
62
+ - Copilot instructions(`init` 写入的):"问用户以前说过的事之前,先 `memory_search`;
63
+ 先试几个关键词变体,搜不到再问。"
64
+
65
+ 也就是说:当对话触及历史决策、偏好、约定时,model **应该**先搜,但没有任何机制强制。
66
+ 这是当前版本的已知缺口——Phase 2C 的原生插件 hooks(如 Claude Code 的
67
+ `UserPromptSubmit`)就是来补这块的。
68
+
69
+ ## 5. 晋升工作流:记忆如何变成团队知识
70
+
71
+ 个人观察变成团队知识只有一条路——**显式晋升,绝不自动同步**:
72
+
73
+ ```
74
+ 个人想法 ──propose──▶ 草稿箱 ──submit──▶ proposed ──┬──promote──▶ approved ──promote──▶ 已发布/共享
75
+ │ │
76
+ --reject --reject
77
+ │ │ (合并前可撤回批准)
78
+ ▼ ▼
79
+ rejected ──resubmit──▶ proposed
80
+ ```
81
+
82
+ - `open-memex propose <id...> --to project`:把一条或多条 personal 记忆**复制**到
83
+ **appdata 草稿箱**进入评审(`review_state: draft`,每条独立新 id,
84
+ `derived_from` 指回 personal 原件)。**复制而非移动——personal 原件保留。**
85
+ 全有或全无:id 有错整批回滚。这一步还不碰 repo——草稿箱 git 看不见、跟分支无关。
86
+ 单人开发可用 `--local-approve` 自批。
87
+ - `open-memex sync-status`:看索引**上次同步的时间和触发方**(会话开始、
88
+ 请求、CLI、submit)、草稿箱(待同步)、repo 里的评审状态
89
+ (`draft / proposed / approved / published / rejected`),以及 repo 里还没
90
+ commit 的记忆文件。你的 agent 会在会话开始、关键节点、你 commit 之后、
91
+ 记忆动作之后跑这个,然后问你哪些草稿(如果有)要同步。你也可以直接说"同步记忆"。
92
+ - `open-memex submit <id...>`:把**你点名的草稿**移入 `<repo>/.ai/open-memex/`,
93
+ 状态变为 `proposed`。它复制文件、改 `review_state`、在**当前分支**做一次
94
+ **本地** git commit——永不自动建分支(建分支你说了算,或 agent 拿到你明确
95
+ 批准走全链时用 `--branch <name>`)。
96
+ 全有或全无、幂等、crash-safe。它打印 `git push` +
97
+ `gh pr create` 命令;如果你的 agent 已经拿到你这次的 Yes,它会自己走完
98
+ push 和 PR。PR 默认 base 是当前分支;`--base` 可改到 `main` 或集成支。
99
+ 分支上同 id 但内容不同——**直接中止**,等人裁决,绝不覆盖。
100
+ - 四个动作分工要分清:**propose 是跨界**(personal → project 草稿箱,
101
+ 唯一跨越"私有/共享"边界的动作);**submit 把草稿搬进 repo**
102
+ (outbox → `.ai/open-memex/`,`draft → proposed`);**promote 只改状态标签**
103
+ (文件一直在 `.ai/open-memex/` 里没动过,只是 `review_state`
104
+ 从 proposed → approved → published)——它不在目录之间搬文件;
105
+ **git 负责运输**(push、PR、合并)。
106
+ - 独立的记忆 PR **只含记忆文件,不含代码**,跟代码 PR 分开评审、分开审计。
107
+ 审的是"这条是真的吗?能给全团队看吗?有没有 secret?"——代码 PR 的 CI 不会查这些。
108
+ 想开分支时你自己建(或 agent 拿到批准后建)。
109
+ - "要求修改"不需要命令:PR 开着的时候,作者直接改同一个文件(自己改,
110
+ 或在 chat 里让 agent 改),commit、push。状态一直是 `proposed`,
111
+ PR 本身就是评审机制。
112
+ - `open-memex promote <id>`:把记忆往阶梯上推一步
113
+ (`proposed → approved → published`)。`--reject --note "..."` 驳回并附注原因
114
+ (合并前也可从 `approved` 驳回,即撤回批准)。每次流转都追加到记忆的
115
+ `review_history`——谁动的、何时、从什么到什么、为什么——PR 关了很久以后
116
+ 还能查到这条评审链。
117
+ PR 合并后,再跑一次 `promote <id>` 标记为 `published`——或者让下面的
118
+ `pr-status --apply` 代劳。(project 记忆提升到 org 级是 Phase 4 的事。)
119
+ - **驳回不删任何东西。**文件留在你的分支上,之后怎么处理由人决定:
120
+ 1. **接受**:关 PR、删分支——文件跟着走(本地索引下次 sync 自己清掉);
121
+ 2. **改完重提**:改文件,跑 `open-memex promote <id> --resubmit`,
122
+ commit、push——同一个 PR 里继续评审;
123
+ 3. **留作记录**:不动它;它带着 `[rejected]` 标签和你的注记一直可见,
124
+ 团队以后能看到"这个考虑过,为啥没要"。
125
+ - `open-memex pr-status [--apply]`:读当前分支的 GitHub PR,把它的状态映射
126
+ 到每条 in-repo 记忆的评审状态——PR merged → `published`,PR approved →
127
+ `approved`(`approved_by` = reviewer),"request changes" 只给建议、从不自动
128
+ 驳回。每条记忆保持自己的独立状态:人做出的 `rejected` 不会被 PR 信号覆盖。
129
+ 默认只报告;`--apply` 在本地执行映射的流转(不 push)。团队在 GitHub 上做完
130
+ 评审,这个命令把结论闭环回记忆索引,不用另造评审 UI。
131
+ - `open-memex resolve [id]`:不带参数列出冲突中的记忆文件;带 id 则尝试
132
+ **字段级 3-way 合并** YAML frontmatter(`tags` 取并集、`updated_at` 取最新、
133
+ 只有一边改了 body 才合)。语义冲突——两边改了同一字段或 body 各改各的——
134
+ **只报告、不自动解决**:文件原样不动,等人来裁决。
135
+ - `list` / `search` 会在 project 记忆旁显示 `[draft]` / `[proposed]` /
136
+ `[approved]` / `[published]` / `[rejected]` 标签;检索会把已评审的知识
137
+ (`approved` / `published`)排在未评审的 outbox 草稿前面——草稿照样能搜到,
138
+ 但不会冒充已审核的结论。
139
+
140
+ ## 6. 同步:git 是搬运工,不是大脑
141
+
142
+ - **写**:`memory_add`(project scope)→ 写 **appdata 草稿箱**,同时更新本地索引。
143
+ **不碰 repo、不自动 commit、不自动 push**。
144
+ - **交**(显式,你说了算):`open-memex submit <id...>` → 本地分支 + 本地 commit
145
+ 进 `.ai/open-memex/`;push/PR 命令打印给你(或你的 agent 拿着你的 Yes 自己做)。
146
+ - **拉** **2B**:`open-memex pull`(必须显式,没有自动)→ git fetch + fast-forward →
147
+ 扫描 `.ai/open-memex/*.md` → 按文件 mtime 合进本地 `index.db`。检索永远走 SQLite,不 walk git。
148
+ - **personal scope**:永远不同步(§1 铁律)。
149
+ - **没 git 的项目**:照常用,project scope 降级为纯本地并明确提示,不会坏掉。
150
+
151
+ ## 7. 安全底线
152
+
153
+ - 记忆默认是**数据**,不是指令——外来内容(同步拉回的、别人晋升的)入库前要过
154
+ admission 检查;共享文件的 prompt-injection 筛查只是启发式的,真正的边界是数据/指令分离。
155
+ - 每条记忆带出身证明:`source` + `confidence` + `via` + 作者。共享写操作进 audit log。
156
+ - pre-commit hook 扫描共享 scope 的 secret,防止 history rewrite。
157
+ - `<private>…</private>` 包裹的内容在写入时直接剥离;命中的 secret 模式做掩码后写入继续。
158
+
159
+ ---
160
+
161
+ *配套设计文档:`docs/V2-DESIGN.md`(D1–D24 决策记录)。*
package/docs/V2-DESIGN.md CHANGED
@@ -25,6 +25,12 @@ Decisions log; Prior Art; solo-dev adoption path.
25
25
  6. **Adapters translate; they never implement memory logic.** All memory logic lives in Core.
26
26
  7. **Memory is the entrance of knowledge, not its final form.** Terminal states are docs / ADRs /
27
27
  AGENTS.md instructions — memory is how knowledge gets captured and found.
28
+ 8. **Distill conversations; do not archive them.** The default unit of memory is the useful
29
+ conclusion from a conversation, not the full transcript: what was learned, how it was resolved,
30
+ and where the authoritative source lives.
31
+ 9. **Portable second brain, not a walled garden.** Long-form personal notes are valid memories when
32
+ the user explicitly saves them; Markdown keeps them readable, exportable, and movable to other
33
+ tools or machines.
28
34
 
29
35
  ---
30
36
 
@@ -40,6 +46,9 @@ Decisions log; Prior Art; solo-dev adoption path.
40
46
  - **MCP is an interface, not the identity.** MCP / CLI / REST / SDK are access layers over the protocol,
41
47
  so the project is never locked to one transport or one agent tool (opencode, VS Code Copilot, Cursor,
42
48
  Claude Code, Windsurf, …).
49
+ - **Second-brain lens:** for individuals, OpenMemex can also be a local Markdown second brain for
50
+ AI-assisted work — similar in spirit to users asking AI to save notes into Obsidian or Notion, but
51
+ with agent recall, scopes, review, and redaction built in from the start.
43
52
  - **Company lens:** at organizational scale the same pain is tribal knowledge — senior engineers'
44
53
  hard-won experience evaporates when they move on, and every incident gets re-debugged by someone
45
54
  new. The current phase therefore prioritizes *capture*: valuable knowledge must land in memory
@@ -132,13 +141,37 @@ canonical_ref: docs/adr-003.md # memory holds a SUMMARY; the doc is canoni
132
141
 
133
142
  ### 3.1 Type taxonomy (content kind)
134
143
 
135
- `preference` `fact` `decision` `lesson` `warning` `workflow` `architecture` `constraint`
136
- `todo` `knowledge` `observation`
144
+ `fact` `preference` `decision` `constraint` `todo` `knowledge` `howto` `gotcha`
145
+ `lesson` `observation` `reference`
137
146
 
138
- `type` describes **what the content is**. `role` describes **how it may be used**.
147
+ `type` describes **what the content is** — single-valued, and it drives behavior
148
+ (lifecycle, review, rendering, retrieval). `role` describes **how it may be used**.
139
149
  A `decision` with `role: knowledge` is retrievable history. Only `role: instruction` may enter
140
150
  instruction context. (Separation adopted from review: mixing usage semantics into `type` was a design smell.)
141
151
 
152
+ One-line definitions:
153
+
154
+ - `fact` — a verifiable atomic statement (timezone, version number, path, account).
155
+ - `preference` — user likes, dislikes, working style.
156
+ - `decision` — a choice made + why; participates in supersede chains.
157
+ - `constraint` — a hard rule that must not be violated (release process, permission boundaries).
158
+ - `todo` — an actionable item with completion state.
159
+ - `knowledge` — declarative knowledge (how a system works, concept explanations).
160
+ - `howto` — steps to accomplish X.
161
+ - `gotcha` — a pitfall: don't do X because Y.
162
+ - `lesson` — a takeaway from experience, including incident postmortems (the incident id goes in `tags`).
163
+ - `observation` — noticed but not yet distilled.
164
+ - `reference` — a pointer to the authoritative doc via `canonical_ref`; the memory holds the summary.
165
+
166
+ A type earns its place only if the system treats it differently. If two candidates
167
+ share lifecycle, retrieval, and rendering, the loser becomes a tag. (D41 merged
168
+ `warning`→`gotcha`, `workflow`→`howto`, `incident`→`lesson`, `architecture`→`knowledge`.)
169
+
170
+ `tags` are retrieval hints, not a second type system: `type` says what the memory is, while tags say
171
+ which topics, tools, subsystems, paths, or incidents it relates to. Write paths should preserve
172
+ human-provided tags and may suggest simple normalized tags (for example `vscode`, `mcp`, `windows`,
173
+ `auth`, `onboarding`) to improve search without changing memory semantics.
174
+
142
175
  ### 3.2 Iron rules
143
176
 
144
177
  - **Iron rule 1 — the instruction gate:** only memories with `role: instruction`
@@ -170,6 +203,34 @@ Content hash + fuzzy match against existing memories. A superseding write does *
170
203
  the old memory becomes `status: superseded` with `superseded_by` pointing forward. History preserved;
171
204
  queries rank `active` first.
172
205
 
206
+ ### 3.5 Conversation distillation
207
+
208
+ OpenMemex does **not** store raw AI chat transcripts by default. A captured memory should usually be
209
+ a short, human-approved artifact distilled from the conversation: the final conclusion, important
210
+ facts, why the answer matters later, and how the issue was resolved. Good distilled memory candidates
211
+ answer some combination of:
212
+
213
+ - What did we learn or decide?
214
+ - What solved the problem, including key commands, files, links, or steps?
215
+ - What mistake or gotcha should the next developer avoid?
216
+ - Which scope owns it: `personal`, `project`, or future `org`?
217
+ - If the information already exists in project docs, where is the authoritative doc?
218
+
219
+ If stable knowledge already lives in a project document, prefer a `type: reference` memory with
220
+ `canonical_ref` pointing at that document over duplicating the full content. Memory should help the
221
+ agent find and apply authoritative docs, not become an outdated second copy of them.
222
+
223
+ Model-suggested captures use `source: inference` and should default to reviewable draft state. The
224
+ agent may propose 1-3 distilled memories at checkpoints or task end, but humans decide whether to
225
+ save them and which scope they belong to. The product goal is remembering the right conclusion at
226
+ the right scope, not remembering everything.
227
+
228
+ Long-form notes are still valid when the user explicitly asks to save a long note, write-up, meeting
229
+ summary, research log, or troubleshooting record. In that case OpenMemex behaves like a Markdown
230
+ second-brain target: store the note as user-authored content, preserve the body, tag it for retrieval,
231
+ and keep the same scope/privacy rules. The distinction is intent: implicit/model-suggested capture
232
+ distills; explicit "save this note" may preserve long-form text.
233
+
173
234
  ---
174
235
 
175
236
  ## 4. Scopes, Visibility & Namespaces
@@ -235,7 +296,7 @@ instructions are never candidates.)
235
296
 
236
297
  ```
237
298
  <project-repo>/
238
- ├── .open-memex/ # default; configurable (alt: .ai/memory/)
299
+ ├── .ai/open-memex/ # default (D23); configurable (alt: .ai/memory/)
239
300
  │ ├── 01K6AB….md # one memory = one file ("reduces unrelated merge conflicts…")
240
301
  │ └── …
241
302
  ├── AGENTS.md # constitution + ONE pointer line to the memory system
@@ -243,9 +304,9 @@ instructions are never candidates.)
243
304
  └── docs/ # human-authored formal docs (ADRs, guides)
244
305
  ```
245
306
 
246
- - The in-repo directory name is **configurable** (`memoryDir` in config): default `.open-memex/`
247
- (brand clarity, no collisions), alternative `.ai/memory/` for teams that prefer the emerging `.ai/`
248
- namespace convention.
307
+ - The in-repo directory name is **configurable** (`memoryDir` in config): default
308
+ `.ai/open-memex/` (D23 — `.ai/` namespace + brand clarity, no collisions),
309
+ alternatives `.open-memex/` and `.ai/memory/`.
249
310
  - `index.db` is **never committed** — rebuildable from markdown.
250
311
  - Org repo layout (Phase 4): `company-memory/{engineering,architecture,decisions,lessons,policies}/…`
251
312
  - AGENTS.md pointer: `> Project memory lives in .open-memex/ — query it with memory_search before answering.`
@@ -294,11 +355,21 @@ instructions are never candidates.)
294
355
  | Implicit (opt-in) | end-of-session "should I remember X?"; implicit captures default to `confidence: low` and appear in a separate list view for batch cleanup (regret window). |
295
356
  | Redaction (hard) | `<private>…</private>` stripped; secret patterns are **masked in place** (first 4 chars kept, rest → `x`) and the write proceeds (D14); pre-commit hook scans shared scopes. |
296
357
 
358
+ **Scope routing on capture:** when the user says "save", "remember", or "note this" without an
359
+ explicit scope, the agent should infer ownership from content and current context. Project-specific
360
+ knowledge (repo commands, architecture, code paths, product decisions, team conventions) routes to
361
+ `project`; personal preferences, private working notes, cross-project habits, or content unrelated to
362
+ the current repo routes to `personal`. Ambiguous captures should ask one short clarification instead
363
+ of guessing. `org` capture remains future/curated; a single user's chat never writes org memory
364
+ directly.
365
+
297
366
  ---
298
367
 
299
368
  ## 9. Sync
300
369
 
301
- - **Git is transport; the local SQLite index is the query layer.** Retrieval never walks git.
370
+ - **Git is a transport, not the product boundary; the local SQLite index is the query layer.**
371
+ Retrieval never walks git. Git/GitHub is the default sharing provider because it gives teams branch,
372
+ PR, review, and audit semantics they already trust, but Core must remain provider-agnostic.
302
373
  - one-memory-one-file ⇒ concurrent edits almost never conflict.
303
374
  - **Pull is explicit** (`open-memex pull`), never automatic on session start — no surprise context changes.
304
375
  `pull` = `fetch` + fast-forward only; never auto-commit/push (hard rule for enterprise environments).
@@ -314,6 +385,13 @@ instructions are never candidates.)
314
385
  - **Git unavailable:** projects without git (or with unreachable remotes) remain fully usable.
315
386
  `personal` scope works everywhere; `project` scope degrades to local-only and `open-memex status`
316
387
  annotates it as such. Sync commands fail with a clear message, never with a broken state.
388
+ - **Portability:** because Markdown is the source of truth, memories should be exportable without
389
+ depending on git. A future `open-memex export` command can archive selected scopes/types/tags into a
390
+ portable zip/tar bundle (markdown + manifest) for moving to another computer or importing into
391
+ another application. Export excludes `visibility: private` memories by default; `--all` / `-a`
392
+ includes everything, for a full personal migration to a new machine. API-backed exporters/providers (Notion, Obsidian-compatible vaults, internal
393
+ knowledge systems, enterprise stores) are extension paths over the same memory files and admission
394
+ rules, not separate products.
317
395
 
318
396
  ---
319
397
 
@@ -421,7 +499,7 @@ Zero-config is survival for an open-source project. The opencode plugin remains
421
499
  Ships in **`0.3.0-alpha`** (with bin/npx user-friendliness polish per §17 adoption path).
422
500
  - **Phase 2B — Team sync.** GitProvider · `propose/promote/resolve` · in-repo dir · 1–2 colleague pilot
423
501
  (pilot project selection is maintainer-private, not tracked in this doc).
424
- Ships in **`0.3.0-beta`**.
502
+ Ships in **`0.4.0`** (was labeled `0.3.0-beta` before 0.3.0 shipped as stable).
425
503
  Embeddings/rerank run as a **parallel benchmark-gated experiment**, not on the critical path.
426
504
  - **Phase 2C — Native agent plugins (candidates, not committed).** Claude Code plugin and/or
427
505
  Codex plugin as hook-enhanced paths over the same MCP tool surface (`SessionStart` →
@@ -456,10 +534,13 @@ requirement: personal data never touches third-party services). Benchmarks to tr
456
534
  | atlaso-labs/codex | Codex marketplace | long-term memory plugin for Codex (hooks + MCP + cloud-sync upsell) | **direct comparable** for a future Codex plugin; their cloud upsell vs our local-first |
457
535
 
458
536
  (Star counts / funding as of Sep 2026 — re-verify before quoting publicly.)
459
- - **Phase 3 — Org layer.** Org memory repo · curator convention · `examples/remote-server/` ·
460
- distill-to-AGENTS.md assist.
537
+ - **Phase 3 — Org layer.** Org memory repo · `examples/remote-server/` ·
538
+ curator convention ✅ `docs/CURATOR.md` (2026-09-29, pulled forward) ·
539
+ distill-to-AGENTS.md assist ✅ `open-memex distill-agents` (2026-09-29, pulled forward) ·
540
+ export/import archive command ✅ `open-memex export` / `import` (2026-09-29, pulled forward, D40).
461
541
  - **Phase 4 — Future, signal-gated.** Cloud `RemoteProvider` customization only on: multi-private-repo
462
- sharing needs, fine-grained ACL, audit/compliance mandates.
542
+ sharing needs, fine-grained ACL, audit/compliance mandates · optional API-backed exporters/providers
543
+ for enterprise knowledge systems.
463
544
 
464
545
  ## 19. Migration v1 → v2
465
546
 
@@ -488,7 +569,7 @@ requirement: personal data never touches third-party services). Benchmarks to tr
488
569
  - **D7** — Embeddings are an optional capability, BM25+CJK is the default. *Rationale: zero-setup
489
570
  default; no mandatory 100MB download or native dependency.*
490
571
  - **D8** — Contested choices become **configurable with a popular default**, not hard-coded.
491
- Applies to: in-repo dir name (default `.open-memex/`), CJK tokenizer (default bigram),
572
+ Applies to: in-repo dir name (default `.ai/open-memex/` per D23), CJK tokenizer (default bigram),
492
573
  embeddings model (default `multilingual-e5-small`). *Rationale: the five-AI review split on all
493
574
  three; maintainers shouldn't burn decision capital where config suffices.*
494
575
  - **D9** — The LAN reference server lives at `examples/remote-server/`. *Rationale: maintainer decision
@@ -597,6 +678,199 @@ requirement: personal data never touches third-party services). Benchmarks to tr
597
678
  keeps the old repo-level behavior for teams where everyone uses open-memex.
598
679
  The instructions carry a guard clause ("ignore this section when the
599
680
  `open-memex` MCP server is not available") as cheap insurance. 2026-09-27.*
681
+ - **D23** — Default in-repo memory dir is `.ai/open-memex/` (configurable via
682
+ `memoryDir`; rejects absolute paths and `..`). *Rationale: `.ai/` is the
683
+ emerging convention for AI-local project state; the `open-memex/` leaf keeps
684
+ brand clarity and avoids collisions with other tools' `.ai/` content.
685
+ 2026-09-27.*
686
+ - **D24** — Project-scope markdown lives in the repo: writes with
687
+ `scope: project` go to `<projectRoot>/<memoryDir>/` (D23 default
688
+ `.ai/open-memex/`); `personal` never leaves appdata. Legacy appdata project
689
+ files are lazily migrated on first write/`syncScope` — the move is guarded by
690
+ the appdata dir name (which *is* the scope key), so it can only ever migrate
691
+ the current scope's files. The index's `file_path` is the single locator for
692
+ reads/deletes (`findMemoryFile`, `forget`); on the near-impossible id
693
+ collision between locations, the in-repo copy wins. *Rationale: one home per
694
+ scope, no silent data loss, no repo pollution before first use.* 2026-09-27.*
695
+ - **D25** — Review workflow semantics (`propose` / `promote` / `resolve`,
696
+ `src/review.ts`): (1) `review_state` frontmatter field
697
+ (`draft → proposed → approved/rejected → published`, default `draft`) plus
698
+ `proposed_by` / `approved_by` / `derived_from` / `review_note` provenance;
699
+ the SQLite index carries `review_state` (schema v6, rebuilt from markdown —
700
+ D1). (2) `propose` **copies** personal → project (new id, never moves — the
701
+ personal original stays private); one call takes several ids (one branch,
702
+ one PR; all-or-nothing — a bad id aborts the whole batch);
703
+ `--local-approve` skips the PR for solo
704
+ devs. (3) `promote` advances exactly one step up the ladder
705
+ (`proposed → approved → published`), `--reject`s with a note (also from
706
+ `approved`, withdrawing approval before merge), or `--resubmit`s a rejected
707
+ memory back to `proposed` for another round; `draft` and terminal states
708
+ refuse. (4) `resolve` lists conflicted memory files, or
709
+ attempts a field-level 3-way merge from git stages 1/2/3 (`tags` union,
710
+ `updated_at` takes latest, body merged only when one side changed); semantic
711
+ conflicts (same field / body changed differently on both sides) are reported
712
+ and the file is left untouched — **never auto-resolved**. (5) A rejection
713
+ never deletes anything: the file stays on the author's branch; the human
714
+ accepts (close PR, delete branch), revises + `--resubmit`s, or keeps it as
715
+ a `[rejected]` record. (6) No git
716
+ automation anywhere in the workflow: no branch creation, no commits, no PRs —
717
+ the commands print the exact next steps for the human. Org-level promotion is
718
+ Phase 4. *Rationale: the review ladder must be explicit and auditable; the
719
+ tool assists merging but a human always decides meaning.* 2026-09-27.*
720
+ - **D26** — **Supersedes D24.** Project-scope markdown has two homes, one per
721
+ lifecycle stage — never silently moved between them. (1) `memory_add` /
722
+ `memory_propose` with `scope: project` write to the **appdata outbox**
723
+ (`memories/<project_key>/<id>.md`, `review_state: draft`); the outbox is
724
+ git-invisible and branch-independent. The D24 lazy migration is deleted —
725
+ appdata project files are legitimate drafts, not legacy. (2) `open-memex
726
+ submit <id...>` / `memory_submit` moves user-named drafts into the repo's
727
+ `<memoryDir>/` (D23), flipping `review_state` to `proposed`: the file now
728
+ follows branches and PRs. `sync-status` / `memory_status` shows both sides
729
+ (outbox drafts, repo review states, uncommitted repo files). (3) The move is
730
+ one-stage-one-place: copy → verify hash → local commit → verify → delete
731
+ outbox original; re-running is idempotent (identical content skips, different
732
+ content aborts). `search` prefers the repo copy when both exist; conflicts
733
+ (same id, different content; push rejected) stop and ask the human — never
734
+ overwrite. *Rationale: an AI agent drafts constantly; the repo should only
735
+ ever see what the human explicitly approved for review. The outbox is the
736
+ agent's desk, the repo dir is the shared table. Approved 2026-09-28.*
737
+ - **D27** — **Revises D25 §6 (no git automation).** `submit` automates the
738
+ *local* half of the git workflow: create `mem/sync-<timestamp>` (or
739
+ `--onto` the current branch for code+memory PRs), copy, `git add` only the
740
+ memory files, local commit, verify. It prints the push + `gh pr create`
741
+ commands for the human — but an agent that already holds the user's Yes for
742
+ this sync carries through push and PR creation without re-asking (each step
743
+ is not a separate approval). Batch submit is all-or-nothing; an empty-branch
744
+ abort rolls the branch back. *Rationale: the old "no git automation" rule
745
+ assumed a human at the keyboard; the agent-driven flow needs local mechanics
746
+ automated while push/PR stay under the user's explicit per-sync Yes/No.
747
+ Approved 2026-09-28.*
748
+ - **D28** — Memory PR base defaults to the **current branch**; the user may
749
+ redirect to `main` or the project's integration branch (`--base`). A
750
+ standalone memory PR and a code+memory PR are both supported — the agent asks
751
+ which one each time; on "with code" it uses `--onto` and never commits
752
+ unrelated staged changes. *Rationale: memory usually reviews against the work
753
+ it describes (current branch); the integration branch is the exception, not
754
+ the default. Approved 2026-09-28.*
755
+ - **D29** — Every review transition is written to a per-memory audit trail
756
+ (`review_history`: at/by/from/to/note) — reject / resubmit / approve /
757
+ publish all append, never overwrite; `propose` and `submit` seed it; merge
758
+ conflict resolution unions both sides' histories. *Rationale: review is a
759
+ decision log, not a flag — "who rejected this and why" must be answerable
760
+ months later. Approved 2026-09-28.*
761
+ - **D30** — Retrieval ranks by review state: approved/published project
762
+ memories outrank unreviewed content; project drafts/rejected sink to the
763
+ bottom and are visibly tagged `[draft]` in search/list/inject output, while
764
+ personal memories (always draft by design) are never demoted or tagged.
765
+ Explicit search still finds drafts — they are deprioritized, not hidden.
766
+ *Rationale: reviewed knowledge should win the context window; drafts stay
767
+ discoverable but never masquerade as vetted. Approved 2026-09-28.*
768
+ - **D31** — Every index sync records when it ran, what triggered it
769
+ (`session` / `request` / `cli` / `submit`) and its stats, in
770
+ `<appdata>/sync-state.json`; `sync-status` shows the last sync first.
771
+ *Rationale: "is my index fresh?" must be answerable without guessing —
772
+ especially across branch switches where the in-repo dir changes underneath.
773
+ Approved 2026-09-28.*
774
+ - **D32** — The branch PR's GitHub state maps back onto each in-repo
775
+ memory's review_state via `pr-status` / `memory_pr_status`: merged PR →
776
+ published, PR approval → approved with `approved_by` = reviewer login,
777
+ changes-requested → suggestion only (never auto-rejects). Report by
778
+ default; `--apply` performs the mapped transitions locally (no push —
779
+ inside the D27 line). Each memory keeps its own state: a human `rejected`
780
+ is never overridden by a PR signal. *Rationale: the PR is where the team
781
+ actually reviews — the mapping closes the loop without inventing new
782
+ review UI. Approved 2026-09-28.*
783
+ - **D34** — The MCP server sends session-start guidance in the handshake
784
+ `instructions`: call `memory_status` at session start (and at work
785
+ checkpoints); if the outbox has drafts, summarize and ask the user which to
786
+ sync; proactive `memory_add`; `memory_search` before asking about the past;
787
+ personal never leaves the machine. *Rationale: the init-written instruction
788
+ files only exist if the user ran `init --client` — the handshake reaches
789
+ every MCP client at connect time. Still advisory: no MCP consumer offers a
790
+ hard session-start hook, and we do not claim otherwise. Approved 2026-09-28.*
791
+ - **D35** — "sync memory" (or "同步记忆") is a natural-language trigger for the
792
+ sync flow: the agent calls `memory_status`, summarizes the outbox drafts, and
793
+ asks the user which ones to sync — same flow as the session-start proposal,
794
+ but user-initiated. Taught in the MCP handshake instructions, the
795
+ init-written instruction files, and the `memory_status` tool description.
796
+ *Rationale: the user should not have to remember command names to sync;
797
+ saying it in words must work. Approved 2026-09-28.*
798
+ - **D36** — `submit` never creates a branch on its own (revises D26/D27): it
799
+ copies the named drafts into `.ai/open-memex/`, commits locally on the
800
+ CURRENT branch, and prints next-step commands. Branch creation is the human's
801
+ call — or the agent's, only with explicit approval for the full chain, via
802
+ `submit --branch <name>` / `memory_submit(branch=...)`. *Rationale: an
803
+ auto-created branch strands the user on it — they forget to switch back.
804
+ After a submit the agent asks ONE follow-up ("want me to create a branch +
805
+ push + open the PR, or will you handle it yourself?") instead of branching
806
+ silently. Checkpoints that trigger `memory_status`: session start, end of a
807
+ work chunk, after the user commits, and after any memory_* action.
808
+ The "sync memory" trigger ALWAYS goes through the `memory_status` tool —
809
+ never by browsing the appdata directory directly. Approved 2026-09-28.*
810
+ - **D33** — Every CLI command answers `open-memex <command> --help` (and `-h`)
811
+ with its own usage, flags, and examples; checked before config/DB load so
812
+ help works even in a broken environment. Unknown commands with `--help`
813
+ fall back to the global usage. *Rationale: AI assistants discover the CLI
814
+ through --help first — a command that silently swallows --help as a flag
815
+ teaches the agent nothing. Approved 2026-09-28.*
816
+ - **D37** — Conversation distillation is the memory unit; transcript storage is
817
+ not the default. Capture should preserve the useful conclusion from a human/AI
818
+ session — what was learned, what resolved the issue, what should be avoided,
819
+ and where the authoritative doc lives — as a short reviewable memory. If the
820
+ knowledge already exists in docs, store a `reference` memory with
821
+ `canonical_ref` instead of duplicating the doc. Tags are retrieval hints, not
822
+ content kinds: `type` classifies the memory, tags describe topics/tools/paths.
823
+ *Rationale: full chat logs are noisy, harder to review, and riskier for
824
+ privacy; the value is remembering the right conclusion at the right scope,
825
+ with enough context for the next human or AI session. Approved 2026-09-28.*
826
+ - **D38** — OpenMemex supports a Markdown second-brain use case while keeping
827
+ distillation as the default for implicit/model-suggested capture. If the user
828
+ explicitly asks to save a long note, meeting summary, troubleshooting record,
829
+ or write-up, preserve it as user-authored Markdown with tags and normal
830
+ scope/privacy rules. Git/GitHub sync is the default team-sharing provider, not
831
+ the product boundary: future export/import archives and API-backed providers
832
+ may move the same markdown memories to other computers, applications, or
833
+ enterprise systems. For generic "save/remember/note this" requests, the agent
834
+ routes project-specific knowledge to `project`, personal or repo-unrelated
835
+ knowledge to `personal`, and asks one clarification if ambiguous; `org` remains
836
+ curated/future, never a direct single-user chat write. *Rationale: users also
837
+ want a local AI-assisted second brain, but portability and ownership must stay
838
+ explicit; sharing mechanisms should be provider choices over the same memory
839
+ model, not the identity of the product. Approved 2026-09-28.*
840
+ - **D39** — `type` and `tags` are complementary axes, not alternatives. `type` is
841
+ single-valued: what the memory IS — it drives behavior (lifecycle, review,
842
+ rendering, retrieval: a `todo` can be completed, a `reference` resolves
843
+ `canonical_ref`, a `decision` participates in supersede chains). `tags` are
844
+ multi-valued: what the memory is ABOUT — pure retrieval hints
845
+ (topics, tools, subsystems, paths, incidents). *Rationale: without type the
846
+ system cannot tell "a todo I must do" from "a fact I must know" even when both
847
+ are tagged `auth`; without tags, cross-cutting retrieval ("everything about
848
+ onboarding") would need a combinatorial type explosion. Type answers "how do I
849
+ handle this?", tags answer "how do I find this?". Approved 2026-09-28.*
850
+ - **D40** — `open-memex export` excludes `visibility: private` memories by
851
+ default; `--all` / `-a` includes everything, for a full personal migration to
852
+ a new machine. *Rationale: the safe default protects privacy; the escape hatch
853
+ keeps the "no walled garden" promise — the user can always take everything
854
+ with them. Approved 2026-09-28.*
855
+ - **D42** — §3.5 checkpoint distillation is taught in the MCP handshake
856
+ instructions (`src/mcp.ts` `SERVER_INSTRUCTIONS`) and the `init`-written
857
+ instruction files (`src/init.ts` `INSTRUCTIONS`): at checkpoints the agent
858
+ DISTILLs the session and proposes 1–3 short memories (conclusion, not
859
+ transcript); nothing is saved without user approval. Approved captures are
860
+ saved via `memory_add` with the new optional `source` param set to
861
+ `"inference"` (default `"tool"`). *Rationale: closes the 0.4.0 TODO from D41 —
862
+ the design's capture loop now reaches the agent. Implemented 2026-09-29.*
863
+ - **D41** — The type taxonomy is reconciled to 11 types with one-line definitions
864
+ (§3.1): `fact` `preference` `decision` `constraint` `todo` `knowledge` `howto`
865
+ `gotcha` `lesson` `observation` `reference`. Merged away: `warning`→`gotcha`,
866
+ `workflow`→`howto`, `incident`→`lesson` (incident id goes in `tags`),
867
+ `architecture`→`knowledge` (tag `architecture`). *Rationale: a type earns its
868
+ place only if the system treats it differently (lifecycle, retrieval,
869
+ rendering); otherwise it is a tag. Fifteen types blur classification and hurt
870
+ agent accuracy — eleven keeps each type's behavioral slot distinct. `lesson`
871
+ is deliberately broader than `incident`: a postmortem's shape (timeline, root
872
+ cause, actions) is a template concern, not a type. Code
873
+ `MEMORY_TYPE_TAXONOMY` updated to match. Approved 2026-09-28.*
600
874
 
601
875
  ## Open Questions
602
876
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-memex",
3
- "version": "0.3.0",
3
+ "version": "0.4.0-alpha.10",
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",
@@ -80,9 +80,9 @@ try {
80
80
  const tools = await req("tools/list", {});
81
81
  const names = (tools.result?.tools ?? []).map((t: any) => t.name).sort();
82
82
  check(
83
- "tools/list has 5 memory tools",
83
+ "tools/list has 11 memory tools",
84
84
  JSON.stringify(names) ===
85
- JSON.stringify(["memory_add", "memory_forget", "memory_list", "memory_search", "memory_supersede"]),
85
+ JSON.stringify(["memory_add", "memory_forget", "memory_list", "memory_pr_status", "memory_promote", "memory_propose", "memory_resolve", "memory_search", "memory_status", "memory_submit", "memory_supersede"]),
86
86
  names.join(","),
87
87
  );
88
88
  const addSchema = tools.result.tools.find((t: any) => t.name === "memory_add").inputSchema;