open-memex 0.8.0-alpha.3 → 0.8.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
@@ -144,8 +144,9 @@ Every write path (tool, keyword hook, CLI `add`) must:
144
144
 
145
145
  Capture-time aliases (D61): `memory_add`/`supersede` may carry up to 4
146
146
  `aliases` (alternate phrasings) into frontmatter; they index in their own FTS
147
- column. Only when `cfg.captureAliases` is on (init asks once, default on) —
148
- when off, passed aliases are dropped. Always pass them through
147
+ column. Always on since 2026-10-07 (Stone: init no longer asks) —
148
+ `cfg.captureAliases` still gates them for backward compatibility with
149
+ installs that set it manually, but new installs never see the question. Always pass them through
149
150
  `normalizeAliases`; the keyword-capture path (no agent in the loop) never
150
151
  invents aliases. Checkpoint-proposal guidance (propose, never auto-draft) is
151
152
  copy, not mechanism: keep the rubric wording in sync across the skill, the
package/CHANGELOG.md CHANGED
@@ -9,7 +9,9 @@ the `@alpha` tag are development snapshots and are not listed individually.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
- - User-defined alias vocabulary as query-time expansion (D81): any memory type may now carry `alias:` (nickname, e.g. 香蕉计划) and `target:` (referent, e.g. 支付系统重构项目) frontmatter — an alias memory defines vocabulary once per scope. At query time, query terms hit the alias registry (exact match, one cheap SELECT per search) and the query is rewritten with the other side of the relation — bidirectional, depth < 3, cycle-safe — then re-run as round 2/3. Rounds merge by normalized-max (`mergeRounds` in `src/retrieve/alias-memory.ts`): each round's BM25 scores are divided by that round's best, so an alias-side champion competes evenly with a literal-side champion; the merged pool is re-ranked and cut to [1.5K, 2K], never back to K. No reindex when an alias changes. Creation: `memory_add` alias/target params, CLI `add --alias/--target`, or frontmatter edit (v1: user-explicit only). Fixture grows to 54 memories / 44 queries (3 bidirectional alias queries): recall@1 0.750, recall@5 **0.977**, MRR 0.857. Also rewords the init `captureAliases` prompt — it was never the synonym switch (D80's expansion always applies).
12
+ ## [0.8.0] - 2026-10-07
13
+
14
+ - User-defined alias vocabulary as query-time expansion (D81): any memory type may now carry `alias:` (nickname, e.g. 香蕉计划) and `target:` (referent, e.g. 支付系统重构项目) frontmatter — an alias memory defines vocabulary once per scope. At query time, query terms hit the alias registry (exact match, one cheap SELECT per search) and the query is rewritten with the other side of the relation — bidirectional, depth < 3, cycle-safe — then re-run as round 2/3. Rounds merge by normalized-max (`mergeRounds` in `src/retrieve/alias-memory.ts`): each round's BM25 scores are divided by that round's best, so an alias-side champion competes evenly with a literal-side champion; the merged pool is re-ranked and cut to [1.5K, 2K], never back to K. No reindex when an alias changes. Creation: `memory_add` alias/target params, CLI `add --alias/--target`, or frontmatter edit (v1: user-explicit only). Fixture grows to 54 memories / 44 queries (3 bidirectional alias queries): recall@1 0.750, recall@5 **0.977**, MRR 0.857. Also removes the init `captureAliases` and keyword-capture questions — both are now always on (Stone 2026-10-07).
13
15
 
14
16
  - Index-time synonym/translation expansion (D80, supersedes D79): the curated map (`src/retrieve/synonyms.ts` — EN dev-domain groups, ZH groups and EN↔ZH translation pairs in one bidirectional list) is now applied at sync/index time — `writeRow` expands each memory's keywords (`expansionVariantsForDoc()`, capped at 32) into the indexed `aliases` column, so any related word matches in a plain round-1 query. The query-time second-chance round is removed (`search()` is a single FTS round; `SearchStats.secondRound` gone); `expandQueryWithSynonyms` stays as a bench-only export. Fixture (51 memories / 41 queries): recall@1 0.732 → 0.756, recall@5 0.854 → **0.976**, MRR 0.791 → **0.859** — all 5 EN↔ZH vocabulary misses rescued at rank 1–3. Map updates need a reindex to backfill.
15
17
 
package/README.md CHANGED
@@ -406,8 +406,8 @@ Three ways memories get in:
406
406
  **Aliases.** Each saved memory can carry up to 4 alternate phrasings
407
407
  (synonyms, another language's equivalent) that are indexed with it, so a
408
408
  question worded differently still finds the memory — "vacation days" finds the
409
- holiday policy. `init` asks once whether to enable this (default on); turn it
410
- off any time with `open-memex config set captureAliases false`.
409
+ holiday policy. Always on — the agent may attach up to 4 per memory, and you
410
+ can pass your own via `open-memex add --aliases`.
411
411
 
412
412
  Three ways to add them: `open-memex add "text" --aliases "phrase one; phrase
413
413
  two"` (semicolon-separated, up to 4); ask the agent ("remember: 小宝贝 is my
package/README.zh-CN.md CHANGED
@@ -263,7 +263,7 @@ open-memex uninstall --yes
263
263
  - **检查点提议**:一个任务收尾时,助手会提议 1–3 条从这轮工作里提炼的候选记忆——决定及其理由、约定、踩过的坑、走过又放弃的路——只有你点头的那几条才存。不会有任何东西在你不知情时被写下来。
264
264
  - **CLI**:`open-memex add "…"`,可选 `--scope` / `--tag` / `--type` / `--aliases`。
265
265
 
266
- **别名。** 每条记忆可以带最多 4 个换一种说法(同义词、另一种语言的对应词),和记忆一起进索引,这样问法不同也能找到——比如搜 "vacation days" 能找到假期政策那条。`init` 时会问一次是否开启(默认开);随时可用 `open-memex config set captureAliases false` 关掉。
266
+ **别名。** 每条记忆可以带最多 4 个换一种说法(同义词、另一种语言的对应词),和记忆一起进索引,这样问法不同也能找到——比如搜 "vacation days" 能找到假期政策那条。永远开启:agent 每条记忆最多可附 4 个,你也可以用 `open-memex add --aliases` 自己加。
267
267
 
268
268
  三种加法:`open-memex add "正文" --aliases "说法一; 说法二"`(分号分隔,最多 4 个);直接跟 agent 说("记住:小宝贝是我的 AI agent,别名加上小沐");或直接改那条记忆的 `aliases:` frontmatter——Markdown 是事实来源,sync 会吃进去。
269
269
 
package/dist/.build-stamp CHANGED
@@ -1 +1 @@
1
- 115d72be5408b8ae31133dbb253693d370cc97b4c4a46ba0ea9522170367f794
1
+ 55d6d5c601b1769ad9c4813aebefcb25ee3b29cab0c6c30f8450d9388fe83a6a
package/dist/cli.js CHANGED
@@ -116,8 +116,7 @@ Flags:
116
116
  --aliases alternate phrasings of this fact (up to 4), indexed with the
117
117
  memory so differently-worded searches still match; separate
118
118
  with ; (commas also accepted — use ; when an alias itself
119
- contains one). Stored only when capture aliases are enabled
120
- (init default)
119
+ contains one). Always on.
121
120
  --alias --target D81: define user vocabulary once — e.g. --alias 香蕉计划
122
121
  --target 支付系统重构项目. Later queries mentioning either side
123
122
  trigger a second search round on the other side (both
package/dist/init.js CHANGED
@@ -811,19 +811,17 @@ export async function initProject(opts) {
811
811
  // Install-time settings (D19). Non-default answers persist to the JSONC
812
812
  // config file; `open-memex config set` changes them later.
813
813
  const patch = {};
814
- const keywordCaptureEnabled = await askBool("Auto-capture keywords like remember… / note that… into memory?", DEFAULT_CONFIG.keywordCaptureEnabled);
815
- if (keywordCaptureEnabled !== DEFAULT_CONFIG.keywordCaptureEnabled)
816
- patch.keywordCaptureEnabled = keywordCaptureEnabled;
814
+ // Keyword auto-capture is always on (Stone 2026-10-07): no init
815
+ // question — there is no reason to turn it off. The
816
+ // `keywordCaptureEnabled` config key is still honored if set manually.
817
817
  const injectOnFirstTurn = await askBool("Inject relevant memories when a session starts?", DEFAULT_CONFIG.injectOnFirstTurn);
818
818
  if (injectOnFirstTurn !== DEFAULT_CONFIG.injectOnFirstTurn)
819
819
  patch.injectOnFirstTurn = injectOnFirstTurn;
820
- // D61: capture-time aliases — the agent may attach its own alternate
821
- // phrasings to each memory so differently-worded questions still find it.
822
- // Distinct from D80's automatic index-time synonym expansion, which
823
- // applies regardless of this setting.
824
- const captureAliases = await askBool("Let the agent attach its own alternate phrasings (aliases) to memories? (The built-in synonym map applies regardless.)", DEFAULT_CONFIG.captureAliases);
825
- if (captureAliases !== DEFAULT_CONFIG.captureAliases)
826
- patch.captureAliases = captureAliases;
820
+ // D61 capture-time aliases are always on (Stone 2026-10-07): the agent
821
+ // may always attach its own alternate phrasings — no init question.
822
+ // (Distinct from D80's automatic index-time synonym expansion, which
823
+ // also always applies.) The `captureAliases` config key is still
824
+ // honored if someone sets it manually; init just never asks anymore.
827
825
  if (Object.keys(patch).length > 0) {
828
826
  const file = saveConfig(patch);
829
827
  console.log(` + settings saved to ${file} (change later with \`open-memex config set <key> <value>\`)`);
package/dist/tools/ops.js CHANGED
@@ -58,7 +58,7 @@ export const scopeArg = z
58
58
  export const aliasesArg = z
59
59
  .array(z.string())
60
60
  .optional()
61
- .describe("Optional: alternate phrasings of this fact — synonyms, another way a question might be worded, equivalents in the user's other language (e.g. 节假日 for 'public holidays'). They are indexed with the memory so differently-worded questions still match. Only used when the install has capture aliases enabled (init default). Send at most 4; extras and blanks are dropped silently, never an error.");
61
+ .describe("Optional: alternate phrasings of this fact — synonyms, another way a question might be worded, equivalents in the user's other language (e.g. 节假日 for 'public holidays'). They are indexed with the memory so differently-worded questions still match. Always on. Send at most 4; extras and blanks are dropped silently, never an error.");
62
62
  export const memoryAddArgs = {
63
63
  content: z.string().min(1).describe("The fact to remember. One idea per memory."),
64
64
  type: z
@@ -242,8 +242,8 @@ export async function addMemory(getScope, cfg, args) {
242
242
  const fm = buildFrontmatter(s, {
243
243
  type: args.type ?? "fact",
244
244
  tags: args.tags ?? [],
245
- // D61: aliases are stored only when the install enables them (init
246
- // asks once, default on); normalize caps at 4 and drops junk silently.
245
+ // D61: aliases are always stored (Stone 2026-10-07: init no longer asks;
246
+ // default on). Normalize caps at 4 and drops junk silently.
247
247
  // Aliases pass through the same redaction as content (write-path
248
248
  // invariant) — an alias carrying a token gets masked, not stored raw.
249
249
  aliases: cfg.captureAliases
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-memex",
3
- "version": "0.8.0-alpha.3",
3
+ "version": "0.8.0",
4
4
  "mcpName": "io.github.stoneskin/open-memex",
5
5
  "description": "Local-first memory layer and protocol for AI coding agents. Markdown source of truth, SQLite FTS5 index, zero cloud.",
6
6
  "keywords": [
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.stoneskin/open-memex",
4
4
  "title": "open-memex",
5
5
  "description": "Local-first memory for AI coding agents — Markdown + SQLite FTS5, shared across editors via MCP.",
6
- "version": "0.7.3",
6
+ "version": "0.8.0",
7
7
  "repository": {
8
8
  "url": "https://github.com/stoneskin/open-memex",
9
9
  "source": "github"
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "open-memex",
16
- "version": "0.7.3",
16
+ "version": "0.8.0",
17
17
  "runtimeHint": "npx",
18
18
  "packageArguments": [
19
19
  {
package/src/cli.ts CHANGED
@@ -133,8 +133,7 @@ Flags:
133
133
  --aliases alternate phrasings of this fact (up to 4), indexed with the
134
134
  memory so differently-worded searches still match; separate
135
135
  with ; (commas also accepted — use ; when an alias itself
136
- contains one). Stored only when capture aliases are enabled
137
- (init default)
136
+ contains one). Always on.
138
137
  --alias --target D81: define user vocabulary once — e.g. --alias 香蕉计划
139
138
  --target 支付系统重构项目. Later queries mentioning either side
140
139
  trigger a second search round on the other side (both
package/src/init.ts CHANGED
@@ -893,28 +893,20 @@ export async function initProject(opts: {
893
893
  // Install-time settings (D19). Non-default answers persist to the JSONC
894
894
  // config file; `open-memex config set` changes them later.
895
895
  const patch: Record<string, unknown> = {};
896
- const keywordCaptureEnabled = await askBool(
897
- "Auto-capture keywords like remember… / note that… into memory?",
898
- DEFAULT_CONFIG.keywordCaptureEnabled,
899
- );
900
- if (keywordCaptureEnabled !== DEFAULT_CONFIG.keywordCaptureEnabled)
901
- patch.keywordCaptureEnabled = keywordCaptureEnabled;
896
+ // Keyword auto-capture is always on (Stone 2026-10-07): no init
897
+ // question — there is no reason to turn it off. The
898
+ // `keywordCaptureEnabled` config key is still honored if set manually.
902
899
  const injectOnFirstTurn = await askBool(
903
900
  "Inject relevant memories when a session starts?",
904
901
  DEFAULT_CONFIG.injectOnFirstTurn,
905
902
  );
906
903
  if (injectOnFirstTurn !== DEFAULT_CONFIG.injectOnFirstTurn)
907
904
  patch.injectOnFirstTurn = injectOnFirstTurn;
908
- // D61: capture-time aliases — the agent may attach its own alternate
909
- // phrasings to each memory so differently-worded questions still find it.
910
- // Distinct from D80's automatic index-time synonym expansion, which
911
- // applies regardless of this setting.
912
- const captureAliases = await askBool(
913
- "Let the agent attach its own alternate phrasings (aliases) to memories? (The built-in synonym map applies regardless.)",
914
- DEFAULT_CONFIG.captureAliases,
915
- );
916
- if (captureAliases !== DEFAULT_CONFIG.captureAliases)
917
- patch.captureAliases = captureAliases;
905
+ // D61 capture-time aliases are always on (Stone 2026-10-07): the agent
906
+ // may always attach its own alternate phrasings — no init question.
907
+ // (Distinct from D80's automatic index-time synonym expansion, which
908
+ // also always applies.) The `captureAliases` config key is still
909
+ // honored if someone sets it manually; init just never asks anymore.
918
910
  if (Object.keys(patch).length > 0) {
919
911
  const file = saveConfig(patch);
920
912
  console.log(
package/src/tools/ops.ts CHANGED
@@ -101,7 +101,7 @@ export const aliasesArg = z
101
101
  .array(z.string())
102
102
  .optional()
103
103
  .describe(
104
- "Optional: alternate phrasings of this fact — synonyms, another way a question might be worded, equivalents in the user's other language (e.g. 节假日 for 'public holidays'). They are indexed with the memory so differently-worded questions still match. Only used when the install has capture aliases enabled (init default). Send at most 4; extras and blanks are dropped silently, never an error.",
104
+ "Optional: alternate phrasings of this fact — synonyms, another way a question might be worded, equivalents in the user's other language (e.g. 节假日 for 'public holidays'). They are indexed with the memory so differently-worded questions still match. Always on. Send at most 4; extras and blanks are dropped silently, never an error.",
105
105
  );
106
106
 
107
107
  export const memoryAddArgs = {
@@ -350,8 +350,8 @@ export async function addMemory(
350
350
  const fm = buildFrontmatter(s, {
351
351
  type: args.type ?? "fact",
352
352
  tags: args.tags ?? [],
353
- // D61: aliases are stored only when the install enables them (init
354
- // asks once, default on); normalize caps at 4 and drops junk silently.
353
+ // D61: aliases are always stored (Stone 2026-10-07: init no longer asks;
354
+ // default on). Normalize caps at 4 and drops junk silently.
355
355
  // Aliases pass through the same redaction as content (write-path
356
356
  // invariant) — an alias carrying a token gets masked, not stored raw.
357
357
  aliases: cfg.captureAliases