open-memex 0.3.0 → 0.4.0-alpha.4
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 +12 -1
- package/README.md +78 -8
- package/README.zh-CN.md +67 -7
- package/dist/cli.js +389 -12
- package/dist/config.js +10 -0
- package/dist/github.js +215 -0
- package/dist/index.js +6 -0
- package/dist/init.js +33 -18
- package/dist/mcp.js +71 -9
- package/dist/paths.js +31 -0
- package/dist/retrieve/inject.js +2 -2
- package/dist/retrieve/search.js +31 -6
- package/dist/review.js +437 -0
- package/dist/store/db.js +3 -2
- package/dist/store/lifecycle.js +28 -13
- package/dist/store/markdown.js +49 -8
- package/dist/store/sync.js +74 -6
- package/dist/submit.js +339 -0
- package/dist/tools/ops.js +163 -6
- package/docs/USER-GUIDE.md +195 -0
- package/docs/USER-GUIDE.zh-CN.md +160 -0
- package/docs/V2-DESIGN.md +129 -6
- package/package.json +1 -1
- package/scripts/smoke-mcp.ts +2 -2
- package/src/cli.ts +401 -13
- package/src/config.ts +16 -0
- package/src/github.ts +257 -0
- package/src/index.ts +6 -0
- package/src/init.ts +33 -17
- package/src/mcp.ts +117 -8
- package/src/paths.ts +32 -0
- package/src/retrieve/inject.ts +2 -2
- package/src/retrieve/search.ts +33 -6
- package/src/review.ts +504 -0
- package/src/store/db.ts +3 -2
- package/src/store/lifecycle.ts +30 -12
- package/src/store/markdown.ts +83 -8
- package/src/store/sync.ts +85 -4
- package/src/submit.ts +411 -0
- package/src/tools/ops.ts +192 -7
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# OpenMemex User Guide
|
|
2
|
+
|
|
3
|
+
> This document describes open-memex's **mental model**: where your memories live,
|
|
4
|
+
> how they flow, and who can see them. The in-repo directory (§2, §6 write path)
|
|
5
|
+
> and the propose → promote → resolve workflow (§5) are implemented on the
|
|
6
|
+
> `V2-dev-p2b` branch; features marked **2B** are still to be built;
|
|
7
|
+
> everything else is 0.3.0 behavior.
|
|
8
|
+
|
|
9
|
+
## In one sentence
|
|
10
|
+
|
|
11
|
+
open-memex is a local-first memory layer: **Markdown is the source of truth;
|
|
12
|
+
SQLite is just a rebuildable index.** Lose the index and you rebuild it.
|
|
13
|
+
Lose the Markdown and it's really gone.
|
|
14
|
+
|
|
15
|
+
## 1. Two scopes: personal vs project
|
|
16
|
+
|
|
17
|
+
Every memory belongs to a scope, which decides **how far it may travel**:
|
|
18
|
+
|
|
19
|
+
| | personal | project |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| Holds | Facts about you: preferences, habits, cross-project info | Facts about the project: stack, conventions, decisions, gotchas |
|
|
22
|
+
| Lives | Only on your machine (see §2) | Local + eligible for team sharing (see §2, §6) |
|
|
23
|
+
| Rule | **Never leaves this machine** | Can be promoted to team knowledge |
|
|
24
|
+
|
|
25
|
+
Keywords route automatically (D18): "remember… / I think… / I like…" (I) → personal;
|
|
26
|
+
"we decided… / we think… / remember for us…" (we) → project.
|
|
27
|
+
|
|
28
|
+
## 2. Two homes: appdata vs the repo directory
|
|
29
|
+
|
|
30
|
+
| | appdata (the desk) | repo `.ai/open-memex/` (the shelf) |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| Location | Windows `%APPDATA%/open-memex`, Linux `~/.local/share/open-memex` | Project root, default `.ai/open-memex/` (D23, configurable via `memoryDir`) |
|
|
33
|
+
| Holds | `memories/personal/` personal memories; project-scope **draft outbox**; `index.db` local index | Submitted project memories (`proposed` and up) |
|
|
34
|
+
| In git? | No | Yes — git is its courier |
|
|
35
|
+
| The index? | `index.db` is rebuildable, **never committed** | No index stored; rebuilt from Markdown on demand |
|
|
36
|
+
|
|
37
|
+
The private notebook (personal) never goes on the shelf. That's an iron rule, not a setting.
|
|
38
|
+
|
|
39
|
+
Project memories start life as **drafts in the appdata outbox** — git-invisible,
|
|
40
|
+
branch-independent. Only drafts you explicitly name move to the shelf, via
|
|
41
|
+
`open-memex submit` (§5). One stage, one home: after a successful submit the
|
|
42
|
+
outbox original is gone; before it, the repo knows nothing.
|
|
43
|
+
|
|
44
|
+
## 3. First-turn injection: how the 8 and 5 are chosen
|
|
45
|
+
|
|
46
|
+
Before the agent's first turn, open-memex injects an `[OPEN-MEMEX]` context block so it
|
|
47
|
+
doesn't need a search call just to get oriented. Defaults: **8 project + 5 personal**
|
|
48
|
+
memories (`maxProjectMemories` / `maxProfileItems`, tunable).
|
|
49
|
+
|
|
50
|
+
Selection has exactly one criterion: **most recently updated first**
|
|
51
|
+
(`ORDER BY updated_at DESC`), minus retracted/archived items, with superseded
|
|
52
|
+
versions resolved away. Not "the N most important" — "the N most recently touched".
|
|
53
|
+
What you just updated is most likely relevant to what you're doing now.
|
|
54
|
+
|
|
55
|
+
Token budget: each item is squeezed to one line (≤240 chars, ~60 tokens), so 8 items
|
|
56
|
+
cost ≈ 500 tokens worst case. Beyond that, diminishing returns — the long tail is what
|
|
57
|
+
`memory_search` is for; stuffing everything into the first turn buries the signal.
|
|
58
|
+
|
|
59
|
+
Raise it: `open-memex config set maxProjectMemories 12`
|
|
60
|
+
|
|
61
|
+
## 4. Later turns: when memory gets searched again
|
|
62
|
+
|
|
63
|
+
First-turn injection happens once. After that there is **no automatic re-search** —
|
|
64
|
+
MCP is request/response; the server cannot push. Whether `memory_search` gets called
|
|
65
|
+
is entirely the model's judgment, guided by two things:
|
|
66
|
+
|
|
67
|
+
- The injected block's footer: "Use the `memory_search` tool to look up more."
|
|
68
|
+
- The Copilot instructions written by `init`: "before asking the user about something
|
|
69
|
+
they may have told you before, call `memory_search` first — try a few keyword
|
|
70
|
+
variants before giving up."
|
|
71
|
+
|
|
72
|
+
So: when the conversation touches past decisions, preferences, or conventions, the
|
|
73
|
+
model *should* search first — but nothing enforces it. This is a known gap in the
|
|
74
|
+
current version; Phase 2C's native plugin hooks (e.g. Claude Code's
|
|
75
|
+
`UserPromptSubmit`) are meant to close it.
|
|
76
|
+
|
|
77
|
+
## 5. Promotion workflow: how a memory becomes team knowledge
|
|
78
|
+
|
|
79
|
+
A personal observation becomes team knowledge by exactly one road —
|
|
80
|
+
**explicit promotion, never automatic sync**:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
personal idea ──propose──▶ outbox draft ──submit──▶ proposed ──┬──promote──▶ approved ──promote──▶ published/shared
|
|
84
|
+
│ │
|
|
85
|
+
--reject --reject
|
|
86
|
+
│ │ (approval withdrawn
|
|
87
|
+
▼ │ before merge)
|
|
88
|
+
rejected ──resubmit──▶ proposed
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- `open-memex propose <id...> --to project`: **copies** one or more personal
|
|
92
|
+
memories into the **appdata outbox** as review candidates (`review_state:
|
|
93
|
+
draft`, each with its own new id, `derived_from` pointing back at the
|
|
94
|
+
personal original). **Copy, not move — the personal original stays.**
|
|
95
|
+
All-or-nothing: a bad id aborts the whole batch. Nothing touches the repo
|
|
96
|
+
yet — the outbox is git-invisible and branch-independent.
|
|
97
|
+
Solo devs can use `--local-approve` to self-approve.
|
|
98
|
+
- `open-memex sync-status`: shows **when the index was last synced** (and
|
|
99
|
+
what triggered it — session start, a request, a CLI run, a submit), the
|
|
100
|
+
outbox (pending sync), the repo review states
|
|
101
|
+
(`draft / proposed / approved / published / rejected`), and any uncommitted
|
|
102
|
+
repo memory files. Your agent calls this at session start and at meaningful
|
|
103
|
+
checkpoints, then asks which drafts (if any) you want synced.
|
|
104
|
+
- `open-memex submit <id...>`: moves **your named drafts** into
|
|
105
|
+
`<repo>/.ai/open-memex/` as `proposed`. It creates `mem/sync-<timestamp>`
|
|
106
|
+
(or stays on the current branch with `--onto` for a code+memory PR), copies
|
|
107
|
+
the files, flips `review_state`, and makes a **local** git commit —
|
|
108
|
+
all-or-nothing, idempotent, crash-safe. It prints the `git push` +
|
|
109
|
+
`gh pr create` commands; if your agent already has your Yes for this sync,
|
|
110
|
+
it carries through push and PR itself. The PR base defaults to the current
|
|
111
|
+
branch; `--base` redirects to `main` or your integration branch.
|
|
112
|
+
Same id with different content on the branch **aborts** — a human decides,
|
|
113
|
+
never auto-overwrite.
|
|
114
|
+
- Keep the four jobs straight: **propose crosses the boundary**
|
|
115
|
+
(personal → project outbox, the only step that copies across);
|
|
116
|
+
**submit moves drafts into the repo** (outbox → `.ai/open-memex/`,
|
|
117
|
+
`draft → proposed`); **promote only flips the status label** on a file
|
|
118
|
+
already in `.ai/open-memex/` (`proposed → approved → published`) — it
|
|
119
|
+
never moves files between directories; **git does the transport**
|
|
120
|
+
(push, PR, merge).
|
|
121
|
+
- A standalone memory PR contains **only memory files, no code**, reviewed and
|
|
122
|
+
audited separately from code PRs. Reviewers check "is this true? is it safe
|
|
123
|
+
to share? any secrets?" — things a code PR's CI never checks. You can also
|
|
124
|
+
ride along in a code PR (`submit --onto <branch>`).
|
|
125
|
+
- "Request changes" needs no command: while the PR is open, the author edits
|
|
126
|
+
the same file (directly, or by asking their agent in chat), commits, and
|
|
127
|
+
pushes. The state stays `proposed`; the PR is the review mechanism.
|
|
128
|
+
- `open-memex promote <id>`: advances the memory one step up the ladder
|
|
129
|
+
(`proposed → approved → published`). `--reject --note "..."` rejects with a
|
|
130
|
+
reason (also allowed from `approved`, before merge — withdrawing approval).
|
|
131
|
+
Every transition is appended to the memory's `review_history` — who moved it,
|
|
132
|
+
when, from what to what, and why — so the review trail survives long after
|
|
133
|
+
the PR is closed.
|
|
134
|
+
After the PR merges, run `promote <id>` once more to mark it `published`
|
|
135
|
+
— or let `pr-status --apply` do it for you (below).
|
|
136
|
+
(Lifting project memories to org level is Phase 4.)
|
|
137
|
+
- **A rejection never deletes anything.** The file stays on your branch; what
|
|
138
|
+
happens next is the human's call:
|
|
139
|
+
1. **Accept**: close the PR and delete the branch — the file goes with it
|
|
140
|
+
(the local index cleans itself up on the next sync);
|
|
141
|
+
2. **Revise and resubmit**: edit the file, run
|
|
142
|
+
`open-memex promote <id> --resubmit`, commit, push — review continues on
|
|
143
|
+
the same PR;
|
|
144
|
+
3. **Keep as a record**: leave it; it stays visible with a `[rejected]` tag
|
|
145
|
+
and your note, so the team can see what was considered and why not.
|
|
146
|
+
- `open-memex pr-status [--apply]`: reads the current branch's GitHub PR
|
|
147
|
+
and maps its state onto each in-repo memory's review state — merged PR →
|
|
148
|
+
`published`, PR approval → `approved` (`approved_by` = the reviewer), review
|
|
149
|
+
"request changes" → a suggestion for you to act on (never auto-rejects).
|
|
150
|
+
Each memory keeps its own state: a human `rejected` is never overridden by
|
|
151
|
+
a PR signal. Report by default; `--apply` performs the mapped transitions
|
|
152
|
+
locally (no push). This is how a team review on GitHub closes the loop back
|
|
153
|
+
into the memory index without new review UI.
|
|
154
|
+
- `open-memex resolve [id]`: with no argument, lists conflicted memory files;
|
|
155
|
+
with an id, attempts a **field-level 3-way merge** of the YAML frontmatter
|
|
156
|
+
(`tags` union, `updated_at` takes latest, body merged when only one side
|
|
157
|
+
changed). Semantic conflicts — both sides changed the same field or the
|
|
158
|
+
body differently — are **reported, never auto-resolved**: the file is left
|
|
159
|
+
untouched for a human to decide.
|
|
160
|
+
- `list` / `search` show a `[draft]` / `[proposed]` / `[approved]` /
|
|
161
|
+
`[published]` / `[rejected]` tag next to project memories in review, and
|
|
162
|
+
retrieval ranks reviewed knowledge (`approved` / `published`) above
|
|
163
|
+
unreviewed outbox drafts — drafts stay findable but never pose as vetted.
|
|
164
|
+
|
|
165
|
+
## 6. Sync: git is the courier, not the brain
|
|
166
|
+
|
|
167
|
+
- **Write**: `memory_add` (project scope) → writes the **appdata outbox** and
|
|
168
|
+
updates the local index. **Never touches the repo, never auto-commits,
|
|
169
|
+
never auto-pushes.**
|
|
170
|
+
- **Submit** (explicit, your call): `open-memex submit <id...>` → local branch
|
|
171
|
+
+ local commit into `.ai/open-memex/`; push/PR are printed for you (or done
|
|
172
|
+
by your agent on your Yes).
|
|
173
|
+
- **Pull** **2B**: `open-memex pull` (always explicit, never automatic) → git fetch +
|
|
174
|
+
fast-forward → scans `.ai/open-memex/*.md` → merges into the local `index.db` by
|
|
175
|
+
file mtime. Retrieval always goes through SQLite, never walks git.
|
|
176
|
+
- **personal scope**: never syncs (§1 iron rule).
|
|
177
|
+
- **Projects without git**: keep working; project scope degrades to local-only with
|
|
178
|
+
a clear notice. Nothing breaks.
|
|
179
|
+
|
|
180
|
+
## 7. Security baseline
|
|
181
|
+
|
|
182
|
+
- Memory is **data** by default, never instructions — external content (pulled from
|
|
183
|
+
sync, promoted by others) passes an admission check before it lands; the
|
|
184
|
+
prompt-injection screen on shared files is heuristic, the real boundary is the
|
|
185
|
+
data/instruction separation.
|
|
186
|
+
- Every memory carries provenance: `source` + `confidence` + `via` + author.
|
|
187
|
+
Shared-scope writes go into an audit log.
|
|
188
|
+
- A pre-commit hook scans shared scopes for secrets and guards against history
|
|
189
|
+
rewrites.
|
|
190
|
+
- `<private>…</private>` sections are stripped at write time; detected secret
|
|
191
|
+
patterns are masked in place and the write proceeds.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
*Companion design record: `docs/V2-DESIGN.md` (decisions D1–D24).*
|
|
@@ -0,0 +1,160 @@
|
|
|
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 会在会话开始和关键节点跑这个,然后问你
|
|
91
|
+
哪些草稿(如果有)要同步。
|
|
92
|
+
- `open-memex submit <id...>`:把**你点名的草稿**移入 `<repo>/.ai/open-memex/`,
|
|
93
|
+
状态变为 `proposed`。它会建 `mem/sync-<timestamp>` 分支(或用 `--onto`
|
|
94
|
+
留在当前分支,跟代码走同一个 PR),复制文件、改 `review_state`、做一次
|
|
95
|
+
**本地** git commit——全有或全无、幂等、crash-safe。它打印 `git push` +
|
|
96
|
+
`gh pr create` 命令;如果你的 agent 已经拿到你这次的 Yes,它会自己走完
|
|
97
|
+
push 和 PR。PR 默认 base 是当前分支;`--base` 可改到 `main` 或集成支。
|
|
98
|
+
分支上同 id 但内容不同——**直接中止**,等人裁决,绝不覆盖。
|
|
99
|
+
- 四个动作分工要分清:**propose 是跨界**(personal → project 草稿箱,
|
|
100
|
+
唯一跨越"私有/共享"边界的动作);**submit 把草稿搬进 repo**
|
|
101
|
+
(outbox → `.ai/open-memex/`,`draft → proposed`);**promote 只改状态标签**
|
|
102
|
+
(文件一直在 `.ai/open-memex/` 里没动过,只是 `review_state`
|
|
103
|
+
从 proposed → approved → published)——它不在目录之间搬文件;
|
|
104
|
+
**git 负责运输**(push、PR、合并)。
|
|
105
|
+
- 独立的记忆 PR **只含记忆文件,不含代码**,跟代码 PR 分开评审、分开审计。
|
|
106
|
+
审的是"这条是真的吗?能给全团队看吗?有没有 secret?"——代码 PR 的 CI 不会查这些。
|
|
107
|
+
也可以搭代码 PR 的车(`submit --onto <branch>`)。
|
|
108
|
+
- "要求修改"不需要命令:PR 开着的时候,作者直接改同一个文件(自己改,
|
|
109
|
+
或在 chat 里让 agent 改),commit、push。状态一直是 `proposed`,
|
|
110
|
+
PR 本身就是评审机制。
|
|
111
|
+
- `open-memex promote <id>`:把记忆往阶梯上推一步
|
|
112
|
+
(`proposed → approved → published`)。`--reject --note "..."` 驳回并附注原因
|
|
113
|
+
(合并前也可从 `approved` 驳回,即撤回批准)。每次流转都追加到记忆的
|
|
114
|
+
`review_history`——谁动的、何时、从什么到什么、为什么——PR 关了很久以后
|
|
115
|
+
还能查到这条评审链。
|
|
116
|
+
PR 合并后,再跑一次 `promote <id>` 标记为 `published`——或者让下面的
|
|
117
|
+
`pr-status --apply` 代劳。(project 记忆提升到 org 级是 Phase 4 的事。)
|
|
118
|
+
- **驳回不删任何东西。**文件留在你的分支上,之后怎么处理由人决定:
|
|
119
|
+
1. **接受**:关 PR、删分支——文件跟着走(本地索引下次 sync 自己清掉);
|
|
120
|
+
2. **改完重提**:改文件,跑 `open-memex promote <id> --resubmit`,
|
|
121
|
+
commit、push——同一个 PR 里继续评审;
|
|
122
|
+
3. **留作记录**:不动它;它带着 `[rejected]` 标签和你的注记一直可见,
|
|
123
|
+
团队以后能看到"这个考虑过,为啥没要"。
|
|
124
|
+
- `open-memex pr-status [--apply]`:读当前分支的 GitHub PR,把它的状态映射
|
|
125
|
+
到每条 in-repo 记忆的评审状态——PR merged → `published`,PR approved →
|
|
126
|
+
`approved`(`approved_by` = reviewer),"request changes" 只给建议、从不自动
|
|
127
|
+
驳回。每条记忆保持自己的独立状态:人做出的 `rejected` 不会被 PR 信号覆盖。
|
|
128
|
+
默认只报告;`--apply` 在本地执行映射的流转(不 push)。团队在 GitHub 上做完
|
|
129
|
+
评审,这个命令把结论闭环回记忆索引,不用另造评审 UI。
|
|
130
|
+
- `open-memex resolve [id]`:不带参数列出冲突中的记忆文件;带 id 则尝试
|
|
131
|
+
**字段级 3-way 合并** YAML frontmatter(`tags` 取并集、`updated_at` 取最新、
|
|
132
|
+
只有一边改了 body 才合)。语义冲突——两边改了同一字段或 body 各改各的——
|
|
133
|
+
**只报告、不自动解决**:文件原样不动,等人来裁决。
|
|
134
|
+
- `list` / `search` 会在 project 记忆旁显示 `[draft]` / `[proposed]` /
|
|
135
|
+
`[approved]` / `[published]` / `[rejected]` 标签;检索会把已评审的知识
|
|
136
|
+
(`approved` / `published`)排在未评审的 outbox 草稿前面——草稿照样能搜到,
|
|
137
|
+
但不会冒充已审核的结论。
|
|
138
|
+
|
|
139
|
+
## 6. 同步:git 是搬运工,不是大脑
|
|
140
|
+
|
|
141
|
+
- **写**:`memory_add`(project scope)→ 写 **appdata 草稿箱**,同时更新本地索引。
|
|
142
|
+
**不碰 repo、不自动 commit、不自动 push**。
|
|
143
|
+
- **交**(显式,你说了算):`open-memex submit <id...>` → 本地分支 + 本地 commit
|
|
144
|
+
进 `.ai/open-memex/`;push/PR 命令打印给你(或你的 agent 拿着你的 Yes 自己做)。
|
|
145
|
+
- **拉** **2B**:`open-memex pull`(必须显式,没有自动)→ git fetch + fast-forward →
|
|
146
|
+
扫描 `.ai/open-memex/*.md` → 按文件 mtime 合进本地 `index.db`。检索永远走 SQLite,不 walk git。
|
|
147
|
+
- **personal scope**:永远不同步(§1 铁律)。
|
|
148
|
+
- **没 git 的项目**:照常用,project scope 降级为纯本地并明确提示,不会坏掉。
|
|
149
|
+
|
|
150
|
+
## 7. 安全底线
|
|
151
|
+
|
|
152
|
+
- 记忆默认是**数据**,不是指令——外来内容(同步拉回的、别人晋升的)入库前要过
|
|
153
|
+
admission 检查;共享文件的 prompt-injection 筛查只是启发式的,真正的边界是数据/指令分离。
|
|
154
|
+
- 每条记忆带出身证明:`source` + `confidence` + `via` + 作者。共享写操作进 audit log。
|
|
155
|
+
- pre-commit hook 扫描共享 scope 的 secret,防止 history rewrite。
|
|
156
|
+
- `<private>…</private>` 包裹的内容在写入时直接剥离;命中的 secret 模式做掩码后写入继续。
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
*配套设计文档:`docs/V2-DESIGN.md`(D1–D24 决策记录)。*
|
package/docs/V2-DESIGN.md
CHANGED
|
@@ -235,7 +235,7 @@ instructions are never candidates.)
|
|
|
235
235
|
|
|
236
236
|
```
|
|
237
237
|
<project-repo>/
|
|
238
|
-
├── .open-memex/ # default; configurable (alt: .ai/memory/)
|
|
238
|
+
├── .ai/open-memex/ # default (D23); configurable (alt: .ai/memory/)
|
|
239
239
|
│ ├── 01K6AB….md # one memory = one file ("reduces unrelated merge conflicts…")
|
|
240
240
|
│ └── …
|
|
241
241
|
├── AGENTS.md # constitution + ONE pointer line to the memory system
|
|
@@ -243,9 +243,9 @@ instructions are never candidates.)
|
|
|
243
243
|
└── docs/ # human-authored formal docs (ADRs, guides)
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
-
- The in-repo directory name is **configurable** (`memoryDir` in config): default
|
|
247
|
-
(
|
|
248
|
-
|
|
246
|
+
- The in-repo directory name is **configurable** (`memoryDir` in config): default
|
|
247
|
+
`.ai/open-memex/` (D23 — `.ai/` namespace + brand clarity, no collisions),
|
|
248
|
+
alternatives `.open-memex/` and `.ai/memory/`.
|
|
249
249
|
- `index.db` is **never committed** — rebuildable from markdown.
|
|
250
250
|
- Org repo layout (Phase 4): `company-memory/{engineering,architecture,decisions,lessons,policies}/…`
|
|
251
251
|
- AGENTS.md pointer: `> Project memory lives in .open-memex/ — query it with memory_search before answering.`
|
|
@@ -421,7 +421,7 @@ Zero-config is survival for an open-source project. The opencode plugin remains
|
|
|
421
421
|
Ships in **`0.3.0-alpha`** (with bin/npx user-friendliness polish per §17 adoption path).
|
|
422
422
|
- **Phase 2B — Team sync.** GitProvider · `propose/promote/resolve` · in-repo dir · 1–2 colleague pilot
|
|
423
423
|
(pilot project selection is maintainer-private, not tracked in this doc).
|
|
424
|
-
Ships in **`0.3.0-beta
|
|
424
|
+
Ships in **`0.4.0`** (was labeled `0.3.0-beta` before 0.3.0 shipped as stable).
|
|
425
425
|
Embeddings/rerank run as a **parallel benchmark-gated experiment**, not on the critical path.
|
|
426
426
|
- **Phase 2C — Native agent plugins (candidates, not committed).** Claude Code plugin and/or
|
|
427
427
|
Codex plugin as hook-enhanced paths over the same MCP tool surface (`SessionStart` →
|
|
@@ -488,7 +488,7 @@ requirement: personal data never touches third-party services). Benchmarks to tr
|
|
|
488
488
|
- **D7** — Embeddings are an optional capability, BM25+CJK is the default. *Rationale: zero-setup
|
|
489
489
|
default; no mandatory 100MB download or native dependency.*
|
|
490
490
|
- **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),
|
|
491
|
+
Applies to: in-repo dir name (default `.ai/open-memex/` per D23), CJK tokenizer (default bigram),
|
|
492
492
|
embeddings model (default `multilingual-e5-small`). *Rationale: the five-AI review split on all
|
|
493
493
|
three; maintainers shouldn't burn decision capital where config suffices.*
|
|
494
494
|
- **D9** — The LAN reference server lives at `examples/remote-server/`. *Rationale: maintainer decision
|
|
@@ -597,6 +597,129 @@ requirement: personal data never touches third-party services). Benchmarks to tr
|
|
|
597
597
|
keeps the old repo-level behavior for teams where everyone uses open-memex.
|
|
598
598
|
The instructions carry a guard clause ("ignore this section when the
|
|
599
599
|
`open-memex` MCP server is not available") as cheap insurance. 2026-09-27.*
|
|
600
|
+
- **D23** — Default in-repo memory dir is `.ai/open-memex/` (configurable via
|
|
601
|
+
`memoryDir`; rejects absolute paths and `..`). *Rationale: `.ai/` is the
|
|
602
|
+
emerging convention for AI-local project state; the `open-memex/` leaf keeps
|
|
603
|
+
brand clarity and avoids collisions with other tools' `.ai/` content.
|
|
604
|
+
2026-09-27.*
|
|
605
|
+
- **D24** — Project-scope markdown lives in the repo: writes with
|
|
606
|
+
`scope: project` go to `<projectRoot>/<memoryDir>/` (D23 default
|
|
607
|
+
`.ai/open-memex/`); `personal` never leaves appdata. Legacy appdata project
|
|
608
|
+
files are lazily migrated on first write/`syncScope` — the move is guarded by
|
|
609
|
+
the appdata dir name (which *is* the scope key), so it can only ever migrate
|
|
610
|
+
the current scope's files. The index's `file_path` is the single locator for
|
|
611
|
+
reads/deletes (`findMemoryFile`, `forget`); on the near-impossible id
|
|
612
|
+
collision between locations, the in-repo copy wins. *Rationale: one home per
|
|
613
|
+
scope, no silent data loss, no repo pollution before first use.* 2026-09-27.*
|
|
614
|
+
- **D25** — Review workflow semantics (`propose` / `promote` / `resolve`,
|
|
615
|
+
`src/review.ts`): (1) `review_state` frontmatter field
|
|
616
|
+
(`draft → proposed → approved/rejected → published`, default `draft`) plus
|
|
617
|
+
`proposed_by` / `approved_by` / `derived_from` / `review_note` provenance;
|
|
618
|
+
the SQLite index carries `review_state` (schema v6, rebuilt from markdown —
|
|
619
|
+
D1). (2) `propose` **copies** personal → project (new id, never moves — the
|
|
620
|
+
personal original stays private); one call takes several ids (one branch,
|
|
621
|
+
one PR; all-or-nothing — a bad id aborts the whole batch);
|
|
622
|
+
`--local-approve` skips the PR for solo
|
|
623
|
+
devs. (3) `promote` advances exactly one step up the ladder
|
|
624
|
+
(`proposed → approved → published`), `--reject`s with a note (also from
|
|
625
|
+
`approved`, withdrawing approval before merge), or `--resubmit`s a rejected
|
|
626
|
+
memory back to `proposed` for another round; `draft` and terminal states
|
|
627
|
+
refuse. (4) `resolve` lists conflicted memory files, or
|
|
628
|
+
attempts a field-level 3-way merge from git stages 1/2/3 (`tags` union,
|
|
629
|
+
`updated_at` takes latest, body merged only when one side changed); semantic
|
|
630
|
+
conflicts (same field / body changed differently on both sides) are reported
|
|
631
|
+
and the file is left untouched — **never auto-resolved**. (5) A rejection
|
|
632
|
+
never deletes anything: the file stays on the author's branch; the human
|
|
633
|
+
accepts (close PR, delete branch), revises + `--resubmit`s, or keeps it as
|
|
634
|
+
a `[rejected]` record. (6) No git
|
|
635
|
+
automation anywhere in the workflow: no branch creation, no commits, no PRs —
|
|
636
|
+
the commands print the exact next steps for the human. Org-level promotion is
|
|
637
|
+
Phase 4. *Rationale: the review ladder must be explicit and auditable; the
|
|
638
|
+
tool assists merging but a human always decides meaning.* 2026-09-27.*
|
|
639
|
+
- **D26** — **Supersedes D24.** Project-scope markdown has two homes, one per
|
|
640
|
+
lifecycle stage — never silently moved between them. (1) `memory_add` /
|
|
641
|
+
`memory_propose` with `scope: project` write to the **appdata outbox**
|
|
642
|
+
(`memories/<project_key>/<id>.md`, `review_state: draft`); the outbox is
|
|
643
|
+
git-invisible and branch-independent. The D24 lazy migration is deleted —
|
|
644
|
+
appdata project files are legitimate drafts, not legacy. (2) `open-memex
|
|
645
|
+
submit <id...>` / `memory_submit` moves user-named drafts into the repo's
|
|
646
|
+
`<memoryDir>/` (D23), flipping `review_state` to `proposed`: the file now
|
|
647
|
+
follows branches and PRs. `sync-status` / `memory_status` shows both sides
|
|
648
|
+
(outbox drafts, repo review states, uncommitted repo files). (3) The move is
|
|
649
|
+
one-stage-one-place: copy → verify hash → local commit → verify → delete
|
|
650
|
+
outbox original; re-running is idempotent (identical content skips, different
|
|
651
|
+
content aborts). `search` prefers the repo copy when both exist; conflicts
|
|
652
|
+
(same id, different content; push rejected) stop and ask the human — never
|
|
653
|
+
overwrite. *Rationale: an AI agent drafts constantly; the repo should only
|
|
654
|
+
ever see what the human explicitly approved for review. The outbox is the
|
|
655
|
+
agent's desk, the repo dir is the shared table. Approved 2026-09-28.*
|
|
656
|
+
- **D27** — **Revises D25 §6 (no git automation).** `submit` automates the
|
|
657
|
+
*local* half of the git workflow: create `mem/sync-<timestamp>` (or
|
|
658
|
+
`--onto` the current branch for code+memory PRs), copy, `git add` only the
|
|
659
|
+
memory files, local commit, verify. It prints the push + `gh pr create`
|
|
660
|
+
commands for the human — but an agent that already holds the user's Yes for
|
|
661
|
+
this sync carries through push and PR creation without re-asking (each step
|
|
662
|
+
is not a separate approval). Batch submit is all-or-nothing; an empty-branch
|
|
663
|
+
abort rolls the branch back. *Rationale: the old "no git automation" rule
|
|
664
|
+
assumed a human at the keyboard; the agent-driven flow needs local mechanics
|
|
665
|
+
automated while push/PR stay under the user's explicit per-sync Yes/No.
|
|
666
|
+
Approved 2026-09-28.*
|
|
667
|
+
- **D28** — Memory PR base defaults to the **current branch**; the user may
|
|
668
|
+
redirect to `main` or the project's integration branch (`--base`). A
|
|
669
|
+
standalone memory PR and a code+memory PR are both supported — the agent asks
|
|
670
|
+
which one each time; on "with code" it uses `--onto` and never commits
|
|
671
|
+
unrelated staged changes. *Rationale: memory usually reviews against the work
|
|
672
|
+
it describes (current branch); the integration branch is the exception, not
|
|
673
|
+
the default. Approved 2026-09-28.*
|
|
674
|
+
- **D29** — Every review transition is written to a per-memory audit trail
|
|
675
|
+
(`review_history`: at/by/from/to/note) — reject / resubmit / approve /
|
|
676
|
+
publish all append, never overwrite; `propose` and `submit` seed it; merge
|
|
677
|
+
conflict resolution unions both sides' histories. *Rationale: review is a
|
|
678
|
+
decision log, not a flag — "who rejected this and why" must be answerable
|
|
679
|
+
months later. Approved 2026-09-28.*
|
|
680
|
+
- **D30** — Retrieval ranks by review state: approved/published project
|
|
681
|
+
memories outrank unreviewed content; project drafts/rejected sink to the
|
|
682
|
+
bottom and are visibly tagged `[draft]` in search/list/inject output, while
|
|
683
|
+
personal memories (always draft by design) are never demoted or tagged.
|
|
684
|
+
Explicit search still finds drafts — they are deprioritized, not hidden.
|
|
685
|
+
*Rationale: reviewed knowledge should win the context window; drafts stay
|
|
686
|
+
discoverable but never masquerade as vetted. Approved 2026-09-28.*
|
|
687
|
+
- **D31** — Every index sync records when it ran, what triggered it
|
|
688
|
+
(`session` / `request` / `cli` / `submit`) and its stats, in
|
|
689
|
+
`<appdata>/sync-state.json`; `sync-status` shows the last sync first.
|
|
690
|
+
*Rationale: "is my index fresh?" must be answerable without guessing —
|
|
691
|
+
especially across branch switches where the in-repo dir changes underneath.
|
|
692
|
+
Approved 2026-09-28.*
|
|
693
|
+
- **D32** — The branch PR's GitHub state maps back onto each in-repo
|
|
694
|
+
memory's review_state via `pr-status` / `memory_pr_status`: merged PR →
|
|
695
|
+
published, PR approval → approved with `approved_by` = reviewer login,
|
|
696
|
+
changes-requested → suggestion only (never auto-rejects). Report by
|
|
697
|
+
default; `--apply` performs the mapped transitions locally (no push —
|
|
698
|
+
inside the D27 line). Each memory keeps its own state: a human `rejected`
|
|
699
|
+
is never overridden by a PR signal. *Rationale: the PR is where the team
|
|
700
|
+
actually reviews — the mapping closes the loop without inventing new
|
|
701
|
+
review UI. Approved 2026-09-28.*
|
|
702
|
+
- **D34** — The MCP server sends session-start guidance in the handshake
|
|
703
|
+
`instructions`: call `memory_status` at session start (and at work
|
|
704
|
+
checkpoints); if the outbox has drafts, summarize and ask the user which to
|
|
705
|
+
sync; proactive `memory_add`; `memory_search` before asking about the past;
|
|
706
|
+
personal never leaves the machine. *Rationale: the init-written instruction
|
|
707
|
+
files only exist if the user ran `init --client` — the handshake reaches
|
|
708
|
+
every MCP client at connect time. Still advisory: no MCP consumer offers a
|
|
709
|
+
hard session-start hook, and we do not claim otherwise. Approved 2026-09-28.*
|
|
710
|
+
- **D35** — "sync memory" (or "同步记忆") is a natural-language trigger for the
|
|
711
|
+
sync flow: the agent calls `memory_status`, summarizes the outbox drafts, and
|
|
712
|
+
asks the user which ones to sync — same flow as the session-start proposal,
|
|
713
|
+
but user-initiated. Taught in the MCP handshake instructions, the
|
|
714
|
+
init-written instruction files, and the `memory_status` tool description.
|
|
715
|
+
*Rationale: the user should not have to remember command names to sync;
|
|
716
|
+
saying it in words must work. Approved 2026-09-28.*
|
|
717
|
+
- **D33** — Every CLI command answers `open-memex <command> --help` (and `-h`)
|
|
718
|
+
with its own usage, flags, and examples; checked before config/DB load so
|
|
719
|
+
help works even in a broken environment. Unknown commands with `--help`
|
|
720
|
+
fall back to the global usage. *Rationale: AI assistants discover the CLI
|
|
721
|
+
through --help first — a command that silently swallows --help as a flag
|
|
722
|
+
teaches the agent nothing. Approved 2026-09-28.*
|
|
600
723
|
|
|
601
724
|
## Open Questions
|
|
602
725
|
|
package/package.json
CHANGED
package/scripts/smoke-mcp.ts
CHANGED
|
@@ -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
|
|
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;
|