agenote 0.2.1__tar.gz

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 (125) hide show
  1. agenote-0.2.1/.gitignore +42 -0
  2. agenote-0.2.1/CHANGELOG.md +282 -0
  3. agenote-0.2.1/LICENSE +21 -0
  4. agenote-0.2.1/PKG-INFO +168 -0
  5. agenote-0.2.1/README.md +140 -0
  6. agenote-0.2.1/completions/_agenote +67 -0
  7. agenote-0.2.1/completions/agenote.bash +76 -0
  8. agenote-0.2.1/completions/agenote.fish +29 -0
  9. agenote-0.2.1/docs/adr/0001-extract-framework-and-adapters.md +50 -0
  10. agenote-0.2.1/docs/adr/0002-cards-split-search-curator.md +31 -0
  11. agenote-0.2.1/docs/adr/0003-core-split-index-orgserde.md +32 -0
  12. agenote-0.2.1/docs/adr/0004-curate-cli-atomic-tools-agent-led.md +45 -0
  13. agenote-0.2.1/docs/adr/0005-link-convention.md +47 -0
  14. agenote-0.2.1/docs/usage.md +208 -0
  15. agenote-0.2.1/injectors/README.md +91 -0
  16. agenote-0.2.1/injectors/claude/README.md +93 -0
  17. agenote-0.2.1/injectors/codex/README.md +66 -0
  18. agenote-0.2.1/injectors/hermes/README.md +52 -0
  19. agenote-0.2.1/injectors/opencode/README.md +50 -0
  20. agenote-0.2.1/injectors/zcode/README.md +56 -0
  21. agenote-0.2.1/pyproject.toml +80 -0
  22. agenote-0.2.1/src/agenote/__init__.py +14 -0
  23. agenote-0.2.1/src/agenote/cards.py +1017 -0
  24. agenote-0.2.1/src/agenote/cli.py +1433 -0
  25. agenote-0.2.1/src/agenote/completions.py +353 -0
  26. agenote-0.2.1/src/agenote/config.py +561 -0
  27. agenote-0.2.1/src/agenote/context.py +536 -0
  28. agenote-0.2.1/src/agenote/core.py +681 -0
  29. agenote-0.2.1/src/agenote/curator.py +343 -0
  30. agenote-0.2.1/src/agenote/distill.py +164 -0
  31. agenote-0.2.1/src/agenote/doctor.py +525 -0
  32. agenote-0.2.1/src/agenote/dream.py +690 -0
  33. agenote-0.2.1/src/agenote/extract/__init__.py +98 -0
  34. agenote-0.2.1/src/agenote/extract/base.py +482 -0
  35. agenote-0.2.1/src/agenote/extract/claude.py +189 -0
  36. agenote-0.2.1/src/agenote/extract/codex.py +175 -0
  37. agenote-0.2.1/src/agenote/extract/crush.py +172 -0
  38. agenote-0.2.1/src/agenote/extract/models.py +43 -0
  39. agenote-0.2.1/src/agenote/extract/omp.py +241 -0
  40. agenote-0.2.1/src/agenote/extract/opencode.py +144 -0
  41. agenote-0.2.1/src/agenote/extract/zcode.py +142 -0
  42. agenote-0.2.1/src/agenote/health.py +542 -0
  43. agenote-0.2.1/src/agenote/inbox_archive.py +288 -0
  44. agenote-0.2.1/src/agenote/index.py +269 -0
  45. agenote-0.2.1/src/agenote/linkcheck.py +126 -0
  46. agenote-0.2.1/src/agenote/lint.py +279 -0
  47. agenote-0.2.1/src/agenote/memory.py +1347 -0
  48. agenote-0.2.1/src/agenote/memory_import.py +337 -0
  49. agenote-0.2.1/src/agenote/memscan.py +248 -0
  50. agenote-0.2.1/src/agenote/orgfmt.py +1574 -0
  51. agenote-0.2.1/src/agenote/orgfmt_cli.py +106 -0
  52. agenote-0.2.1/src/agenote/orgserde.py +350 -0
  53. agenote-0.2.1/src/agenote/projector.py +468 -0
  54. agenote-0.2.1/src/agenote/ranking.py +136 -0
  55. agenote-0.2.1/src/agenote/reconcile.py +500 -0
  56. agenote-0.2.1/src/agenote/safeio.py +202 -0
  57. agenote-0.2.1/src/agenote/search.py +466 -0
  58. agenote-0.2.1/src/agenote/shim.py +97 -0
  59. agenote-0.2.1/src/agenote/viz/__init__.py +5 -0
  60. agenote-0.2.1/src/agenote/viz/cli.py +334 -0
  61. agenote-0.2.1/src/agenote/viz/data.py +332 -0
  62. agenote-0.2.1/src/agenote/viz/html/__init__.py +66 -0
  63. agenote-0.2.1/src/agenote/viz/html/charts.js +689 -0
  64. agenote-0.2.1/src/agenote/viz/html/core.js +306 -0
  65. agenote-0.2.1/src/agenote/viz/html/force.js +361 -0
  66. agenote-0.2.1/src/agenote/viz/html/interact.js +269 -0
  67. agenote-0.2.1/src/agenote/viz/html/skeleton.html +162 -0
  68. agenote-0.2.1/src/agenote/viz/html/style.css +1437 -0
  69. agenote-0.2.1/src/agenote/viz/html/template.py +49 -0
  70. agenote-0.2.1/tests/__init__.py +0 -0
  71. agenote-0.2.1/tests/contracts/get.txt +12 -0
  72. agenote-0.2.1/tests/contracts/health.txt +13 -0
  73. agenote-0.2.1/tests/contracts/memory-list.txt +1 -0
  74. agenote-0.2.1/tests/contracts/review.txt +11 -0
  75. agenote-0.2.1/tests/contracts/stats.txt +22 -0
  76. agenote-0.2.1/tests/extract/__init__.py +0 -0
  77. agenote-0.2.1/tests/extract/conftest.py +114 -0
  78. agenote-0.2.1/tests/extract/test_adapter_integration.py +190 -0
  79. agenote-0.2.1/tests/extract/test_adapter_internal_value_error.py +261 -0
  80. agenote-0.2.1/tests/extract/test_base.py +376 -0
  81. agenote-0.2.1/tests/extract/test_omp.py +134 -0
  82. agenote-0.2.1/tests/extract/test_partial_install.py +320 -0
  83. agenote-0.2.1/tests/extract/test_phase1c.py +794 -0
  84. agenote-0.2.1/tests/test_agent_registry.py +97 -0
  85. agenote-0.2.1/tests/test_completions_derived.py +206 -0
  86. agenote-0.2.1/tests/test_config.py +155 -0
  87. agenote-0.2.1/tests/test_context.py +375 -0
  88. agenote-0.2.1/tests/test_contracts_text.py +132 -0
  89. agenote-0.2.1/tests/test_distill_curator.py +131 -0
  90. agenote-0.2.1/tests/test_doctor.py +57 -0
  91. agenote-0.2.1/tests/test_doctor_host_memory.py +215 -0
  92. agenote-0.2.1/tests/test_dream_reconcile.py +179 -0
  93. agenote-0.2.1/tests/test_el_contract.py +124 -0
  94. agenote-0.2.1/tests/test_error_boundary.py +125 -0
  95. agenote-0.2.1/tests/test_health_collab.py +62 -0
  96. agenote-0.2.1/tests/test_health_metrics.py +87 -0
  97. agenote-0.2.1/tests/test_index_orgserde.py +177 -0
  98. agenote-0.2.1/tests/test_linkcheck.py +132 -0
  99. agenote-0.2.1/tests/test_lint_json.py +83 -0
  100. agenote-0.2.1/tests/test_memory_budget.py +155 -0
  101. agenote-0.2.1/tests/test_memory_export.py +205 -0
  102. agenote-0.2.1/tests/test_memory_import.py +485 -0
  103. agenote-0.2.1/tests/test_memory_isolation.py +515 -0
  104. agenote-0.2.1/tests/test_memory_migrate.py +116 -0
  105. agenote-0.2.1/tests/test_memory_model.py +173 -0
  106. agenote-0.2.1/tests/test_memory_revalidate.py +166 -0
  107. agenote-0.2.1/tests/test_memory_stale.py +44 -0
  108. agenote-0.2.1/tests/test_memscan.py +182 -0
  109. agenote-0.2.1/tests/test_noise_and_fingerprint.py +126 -0
  110. agenote-0.2.1/tests/test_org_property_drawer.py +485 -0
  111. agenote-0.2.1/tests/test_orgfmt_fail_report.py +89 -0
  112. agenote-0.2.1/tests/test_orgfmt_markers.py +138 -0
  113. agenote-0.2.1/tests/test_orgfmt_zh_style.py +312 -0
  114. agenote-0.2.1/tests/test_orgserde_safeio.py +252 -0
  115. agenote-0.2.1/tests/test_provenance.py +187 -0
  116. agenote-0.2.1/tests/test_q4_projectors.py +150 -0
  117. agenote-0.2.1/tests/test_ranking.py +107 -0
  118. agenote-0.2.1/tests/test_reconcile_kb_titles.py +100 -0
  119. agenote-0.2.1/tests/test_safeio.py +230 -0
  120. agenote-0.2.1/tests/test_search_curator.py +466 -0
  121. agenote-0.2.1/tests/test_sweep_touch.py +152 -0
  122. agenote-0.2.1/tests/test_tech_sync.py +584 -0
  123. agenote-0.2.1/tests/test_type_gate.py +178 -0
  124. agenote-0.2.1/tests/test_unused_filter.py +41 -0
  125. agenote-0.2.1/tests/test_viz_serve.py +68 -0
@@ -0,0 +1,42 @@
1
+ # SPDX-FileCopyrightText: 2026 BrokenShine <xchai404@gmail.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ # Python bytecode
6
+ __pycache__/
7
+ *.pyc
8
+
9
+ # 构建产物
10
+ build/
11
+ dist/
12
+ *.egg-info/
13
+ *.whl
14
+ *.tar.gz
15
+
16
+ # uv / venv
17
+ .venv/
18
+ .python-version
19
+
20
+ # IDE
21
+ .vscode/
22
+ .idea/
23
+
24
+ # 工具状态(mimosa 安全扫描 / zcode 子智能体工作产物)
25
+ .mimosa/
26
+ .agents/*
27
+ !.agents/skills/
28
+
29
+ # pytest
30
+ .pytest_cache/
31
+ .zcode/plans/
32
+
33
+ # ─── monorepo(ADR 0005)─────────────────────────────────────────
34
+ # 每个包各自可能有构建/工具产物,根规则已覆盖 __pycache__ / .venv /
35
+ # dist / .mimosa;此处补 JS 侧与 elisp 侧。
36
+ node_modules/
37
+ *.elc
38
+
39
+ # spec 代码生成:生成物**提交进仓**(插件独立安装,运行时不能依赖 monorepo),
40
+ # 故此处**不**忽略 packages/ 下任何文件。漂移由 CI 的 `check-generated` 拦截。
41
+ # 校验和文件由 tools/release 写出,可入库。
42
+
@@ -0,0 +1,282 @@
1
+ # Changelog
2
+
3
+ 本项目的所有显著变更记录在此。
4
+
5
+ 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本管理遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.2.1] - 2026-10-12
10
+
11
+ 记忆质量门禁三件套:出处溯源、KB 链接完整性检查、记忆条目预算控制——参照 Cognition agent-memory-repo,把记忆质量押在读写纪律而非检索算法。
12
+
13
+ ---
14
+
15
+ ### Added
16
+
17
+ - **`--source` / `--added` 出处溯源**:`add` 与 `memory --add` 接受自由文本出处与录入日期,落盘为 `SOURCE` / `ADDED` 属性;`update --source/--added` 可改写,`search [--json]` 回显,旧卡缺省显空不回填(a308a46)
18
+ - **`agenote doctor` 两项新检查**:`memory-entry-budget` 发现超预算条目(行数/字符数阈值走 `[memory]` 配置,只报告不自动修——自动修是 ADR-0004 禁止的语义决策);新模块 `linkcheck.py` 做条目级链接完整性检查(a308a46)
19
+ - **session 简报预算截断**:超限记忆条目按 `[context].session_entry_budget_chars`(默认 2000)截断并打行内标记 `…[单条超预算已截断]`,`--json` 输出 `truncated=true`(a308a46)
20
+
21
+ ### Fixed
22
+
23
+ - **`doctor` 预算检查误报**:跳过 deprecated 节归档条目,与 session 简报排除口径一致——只归档不清块不再挡住回绿(2d6d4b2)
24
+
25
+ ## [0.2.0.4] - 2026-10-03
26
+
27
+ `agenote lint --fix` 幂等性修复:全量跑两轮仍在改同一批文件的振荡根因。
28
+
29
+ ### Fixed
30
+
31
+ - **`lint --fix` 不幂等,每轮重改同一批文件**(`orgfmt.py` `_fix_ellipsis` + `_zh_style` 阶段顺序,`lint.py` `cmd_lint`)。两处缺陷互相咬合:
32
+ - `_fix_ellipsis` 的正则 `…+` 把上一轮产物「……」整体再匹配一次并重复报变更,文本却不变——`changes` 与实际文本修改脱钩。
33
+ - R5 原先排在 R2/R3 **之后**:ASCII「...」转成「……」后,两侧空格已无规则收尾,永久残留;下一轮 R2 再删、R5 再命中旧产物,形成两轮一循环的振荡。
34
+ - `cmd_lint` 只检查 `fmt_changes` 非空即报 format issue,不校验文本是否真的变了。
35
+ - 修法:R5 提前到 R2/R3 之前(省略号归一化先于空格规则)、已是「……」的形态不再报变更、`cmd_lint` 增加 `new_text == text` 时清空 `fmt_changes` 的幂等守卫。
36
+ - 实测(541 张卡 + MEMORY.org + 记忆文件共 550 个 .org):修复前 `lint --fix` 连跑两轮各改 348 个文件、3614 处 issue;修复后第二轮零变更,仅 MEMORY.org 因历史上首次格式化需 3 轮收敛。
37
+ - 受影响的行为变化:ASCII 省略号转换后两侧空格一并收尾(`so...that...` → `so……that……`,原先保留一个空格)。中文标点旁禁空格本就是上游规范,旧期望是振荡中间态;elisp 侧与 golden 快照均不依赖该空格。
38
+ - 测试新增 4 项省略号幂等回归(`tests/test_orgfmt_zh_style.py`),总数 489 全绿;注入器 selftest 30/30、codegen --check、release build 均通过。
39
+
40
+ ## [0.2.0.3] - 2026-09-29
41
+
42
+ orgfmt 两条主线:中文技术文档规范阶段(`_zh_style`)落地——8 项自动修复 + 4 项只报不改;内联标记在 Emacs 中的渲染修复——外侧全角紧贴、内侧边界空格、Markdown 双星混写与富文本块漏修根因。
43
+
44
+ ### Added
45
+
46
+ - **orgfmt 中文技术文档规范阶段**(`_zh_style`,`orgfmt.py` 新增阶段 7):把 zh-tech-doc-style-guide 的硬规则落到 Markdown 与 Org 两种格式,规则只写一份(复用已有的 `_iter_lines_with_block_state` 状态机,org block 与 ``` 围栏都识别)。自动修复 8 项——全角空格、全角标点旁多余空格、中英文之间补空格、破折号两侧空格、英文省略号转中文六点、连续感叹号、序数词「数字、」转「数字.」、数值与单位之间补空格;只报不改 4 项(的/地/得、顿号误用于分句、繁体用语、不规范缩略语)。保护:代码块、表格、drawer、org 元数据行(DEADLINE/SCHEDULED 的 repeat cookie、`:EFFORT:` 属性值)、行内公式一律不动。`orgfmt --check` 天然即 lint 门禁(退出码=问题数)。
47
+ - 测试 452 → 454(`tests/test_orgfmt_zh_style.py` 29 项:R1-R8 逐规则 + 幂等 + 保护 + L1-L4 只报不改)。
48
+
49
+ ### Fixed
50
+
51
+ - **orgfmt 内联标记在 Emacs 中不渲染的系统性缺陷**(`_fix_inline_markers` / `_fix_marker_spacing`,`orgfmt.py`):两类失效形态——外侧(全角标点或汉字紧贴标记,如 `:=foo=`、「(=X=)」)补齐两侧空格;内侧(`= foo =`)去边界空格,仅限单一无空白 token,公式类含空白内容保守保留。新增双星混写收敛(Markdown 式 `**X* *` / `**X**` → org 单星 `*X*`;verbatim 内嵌字面双星如 `=**argv=` 不动)。防误伤:多标记行跨段错配对拒绝重扫、ASCII 邻接(`word=foo=`)不补空格、含中文句读的配对判为错配、双星规则排除行首标题与 C 指针。
52
+ - **`_iter_lines_with_block_state` 富文本块误判**:状态机把 note/tip/quote 等一切都当代码块跳过,块内正文(说明性文字的常见载体)的标记修复全部漏改。新增 `code_blocks_only` 参数区分代码类块(src/example/comment/export)与富文本块,`_fix_inline_markers` 采用后者口径。
53
+ - 测试新增 `tests/test_orgfmt_markers.py`(19 项:内外侧修复、错配对保护、双星收敛与周界豁免、块状态口径),总数 479 全绿。
54
+
55
+ - `_normalize_blank_lines` 状态机漏掉文件首行:状态更新整块在 `if out:` 内,导致以 `:PROPERTIES:` 或 `#+begin_src` 开头的文件状态永不置位,drawer 内被插入空行。状态推进抽为 `_advance_blank_state()` 纯函数,首行走同一路径。
56
+ - `_classify_line` 不识别 markdown ``` 围栏:非 strict 模式下围栏内容被当 normal 段落,行间被插入空行。新增 `md_fence` 行类型与 `in_fence` 状态跟踪。
57
+ - orgfmt 与 Guix-configs `tools/doc-punct.py` 的重叠规则口径不一致(两工具交替使用时结果不同):
58
+ - 省略号原先一刀切转,会把 `v1.2.3`、`v2...v3` 版本号/范围误转;改为三点两侧挨数字时跳过(`so...that...` 这类句型省略两侧是单词,仍转)。
59
+ - 中英补空格原先只跳表格行,会在 `[connection]段`、`0=disable省电`、行首标记后误插空格;逐位扫描 + `_skip_zh_latin_at()` 位置判断,与 doc-punct 同口径。
60
+
61
+ ## [0.2.0.2] - 2026-09-26
62
+
63
+ 首次真实规模导入(332 条多源记忆)暴露 import 写入管道缺陷:两处修复、一处回归测试强化、一处假阴性测试修正。reviewer 对抗性审查(修复前代码对比复现)核销通过。
64
+
65
+ ### Fixed
66
+
67
+ - **`memory --import` 多条目同节落位坐标错位**(`memory_import.py` `_write_entries`):含换行的整块条目作为单元素 insert,而节定位行号是 join 后文本行坐标——列表出现多行块元素后两坐标系错位,`_find_section_end` 扫不到下一节行便返回 `len(lines)`,同节第 2 条起全部尾插到文件尾并落入 deprecated 终态节,且节文本不含已插条目使 ID 恒为同值(332 条真实导入出现 230 条损坏、P002 重复 222 次)。改为按行展开插入,使列表元素坐标与文本行坐标恒一致。F/U 路径(首节/新建尾节)碰巧免疫,故 v0.2.0 起未暴露。
68
+ - **同族:import body 逐行缩进防结构注入**:body 只缩进首行时,源侧 markdown 的 `* `/`** ` 行裸写进 org 顶层会被解析成伪顶级节/伪条目,污染 SSOT 并经 export/context 外流(对抗探针实测伪造出 F999 条目);改为逐行缩进脱离结构语法。
69
+ - **条目解析剔除 drawer 结构行**(`memory.py` `_iter_memory_entries`):`:PROPERTIES:`/`:END:` 无属性值、不匹配 `:KEY: value` 属性正则,被当正文收进 body;`memory --add` 条目有 `#` 钩子行兜底(hook 优先)而 import 条目无,残渣直接成为 `agenote context` 注入首行。
70
+ - **修正 `test_cli_context_on_fresh_kb_writes_nothing` 假阴性隔离**:agenote 域 ctx 路径派生自模块常量 `AGENOTE_ROOT`(import 时固化),patch `core.KB_ROOT` 不生效,此前靠真实 KB 为空碰巧通过;改为 patch `core.AGENOTE_ROOT`。
71
+ - 落位回归测试 fixture 改为历史触发形状(条目进既有中间节 + 末节 deprecated),已验证在修复前代码上必失败(原 fixture 的 else 新建末节路径锁不住主 bug)。测试 454 → 459。
72
+
73
+ ## [0.2.0.1] - 2026-09-25
74
+
75
+ 对照 agent 记忆系统研究报告补齐记忆模型:项目/工作区分区隔离、受限条目不离开 SSOT、写入侧 secret 门禁与生命周期元数据。
76
+
77
+ ### Added
78
+
79
+ - **`:PROJECT:` 全类型分区键**(`memory.py` / `context.py` / `projector.py` / `memory_import.py` / `memscan.py`):memscan 提取的 `projects/<slug>` 原值经 import 落盘;`project_key_matches` 判定扁平名 / `-<16hex>` 工作区哈希后缀 / claude 消毒路径三形态 slug;context 注入与 export 投影对带分区键的 U/F/P/E/R 条目统一按当前项目门禁——无法确定项目上下文时不注入、不投影。linked git worktree 的 `.git` gitfile 解析回主仓根,worktree 与主仓共享项目身份。
80
+ - **`:SENSITIVITY:` 受限标记**:非空即不注入不投影、仅存 SSOT(`context._load_entries` 与 `projector._select_entries` 同口径排除);`memory --add --sensitivity <LEVEL>` 落盘;`--list` / `--json` 以 sensitive 标记可见,不回显内容。
81
+ - **写入侧 secret 门禁**(`core.py` `gate_secret_write` / `warn_secret_write`):与 import/export 共用 `SECRET_PATTERNS` 和 `[memories].secret_scan_enabled` 开关。`add` / `update --append|--stdin` / `memory --add` 直入 SSOT 命中即拒写;`inbox` 草稿捕获温和告警;`inbox-archive` 逐条跳过不阻断整批;四路均有 `--allow-secret` 显式豁免。
82
+ - **doctor `kb-secrets` 事后审计**:扫描 MEMORY.org / 卡片 / inbox 的密钥形态内容,只报类别与文件名不回显值——写侧门禁的存量兜底。
83
+ - **记忆生命周期字段**:`memory --touch` 递增 `USAGE_COUNT`;`--archive` / `--supersede` 落 `ARCHIVED_AT` / `SUPERSEDED_BY` 墓碑。
84
+
85
+ ### Fixed
86
+
87
+ - `cmd_touch` 单次调用 USAGE_COUNT 计两次:默认路径连续两次 `touch_card`(LAST_USED + LAST_VERIFIED),`touch_card` 新增 `count` 参数,第二次只刷时间戳;`count=False` 调用不再消耗 session 幂等键。
88
+ - 项目作用域门禁传播:P 条目经项目索引行 PATH 命中后,`:PROJECT:` 指向索引行名字的条目可被正确放行(`_scope_gate` 纳入已命中项目身份集合)。
89
+ - 测试 404 → 425(新增 `tests/test_memory_isolation.py`:分区/敏感/secret/生命周期回归 21 项)。
90
+
91
+ ## [0.2.0] - 2026-09-25
92
+
93
+ 跨 agent 记忆注入架构(C 线)落地:agenote 保持纯粹 CLI,各宿主插件/hook 实时调 CLI 注入记忆简报,替代宿主自带记忆系统;含上一轮实现审查的 22 项修复闭环。
94
+
95
+ ### Added
96
+
97
+ - **`agenote context` 注入简报命令**(`context.py`):`--mode session` 会话简报(U/E/P/F/R 保头降级选集、预算硬上限恒字符、末尾固定「如何查更多」指引)与 `--mode recall` 召回(BM25 + 分数下限 8.0(真实语料 600 样本标定)+ topk + 最短 query 门槛);三态语义——ok 首行 marker `<!-- agenote-context v1 ... -->`(无时间戳,幂等可识别),empty/disabled 输出零字节;`--project` 确定性三步匹配(精确路径 → git root basename → 不猜);只读免锁、不进 `MUTATING_COMMANDS`、全新 KB 不写骨架。
98
+ - **`[injection]` / `[injection.hosts]` 配置节**(`config.py`):总开关 `enabled`、默认预算、会话累计预算、召回参数与 per-host 平铺开关键;`AGENOTE_INJECTION_ENABLED=false` 一键全关,任何注入器随之静默熄火——开关的单一真相源在 agenote 侧。
99
+ - **doctor 宿主记忆六项检测**(`doctor.py`):zcode/claude/codex/omp/hermes 五宿主自带记忆开关只读探测(未安装跳过)+「投影 targets 非空且注入开启」双通道并存警告(附退役指引);既有 8 项检测措辞不变。
100
+ - **`injectors/` 注入器包**:zcode/claude 成品(SessionStart 简报 + UserPromptSubmit 每轮 recall;追加型三件套——指纹未变不注、recall 门槛、单会话累计预算;状态文件 `~/.cache/agenote/injectors/<host>-<sid>.json`)、codex/opencode recipe 模板(experimental/待信任确认项如实标注)、共享库 `lib.sh` 与 `selftest.sh`(30 项断言)。
101
+ - **shim `context` 子命令透传**(`shim.py`):pi 侧 `agenote-cli` 入口支持 context(参数与主 CLI 对齐),pi 注入链路打通。
102
+ - **import 条目元数据闭环**(`memory_import.py` / `memscan.py`):条目落盘 `:ORIGIN_PATH:`、E 类落 `:MACHINE:`(切机重验对自家产物生效)、`:NEEDS_REVIEW:` 落盘;新增 `memory --migrate` 一次性迁移命令(ORIGIN_ID 相对化 + EXPIRES_AFTER 天数化)。
103
+ - 测试 329 → 404(context 三态/预算裁剪/doctor 探测/orgserde 与 safeio 等)。
104
+
105
+ ### Changed
106
+
107
+ - **注入主通道裁决**:宿主插件/hook 实时调 `agenote context` 注入为主通道(五宿主有一等挂接点),`[memories.targets]` 文件投影降级为遗留通道——宿主记忆目录是共享读写沙箱,投影会被宿主抽取代理改写(结构性双写冲突)。
108
+ - ORIGIN_ID 改为相对源根路径派生(`sha256(AGENT:相对路径:标题)[:16]`),源根改名/换机不再全量碎裂;读取侧双算兼容旧绝对路径 ID(保留一个版本)。
109
+ - EXPIRES_AFTER 改天数语义(自 UPDATED/CREATED 起算);旧日期形态兼容判定 + 迁移警告。
110
+ - dream snapshot 未变化时清空 candidates 并指回游标(`unchanged` 与「无候选」可区分,消费方不再重复复核)。
111
+ - `memory --add --type P` 合一到 MEMORY.org 单一写路径(侧文件路径退役);secret 扫描清单合一到 `core.SECRET_PATTERNS`;聚合投影文件去日期戳(跨天字节级幂等);时效口径统一(VALIDATED_AT → UPDATED → CREATED)。
112
+ - KB 标题收集合一到 `orgserde.collect_card_titles`(BOM 剥离)。
113
+
114
+ ### Fixed
115
+
116
+ - **回声防护闭环(P0)**:import 侧消费投影 marker(`x-agenote-projected` / `x-agenote-pointer`)拒收自身投影,targets 前缀排除改 realpath 归一化(symlink/大小写免疫)——清空 targets 配置后投影副本不再被读回 MEMORY.org 造成递归污染。
117
+ - import 的 BOM/CRLF 归一化:CRLF 源文件不再把 `\r` 字节写入 MEMORY.org、frontmatter 不再整段退化。
118
+ - E 类切机重验失效(import 产物缺 `:MACHINE:` 导致 `--revalidate` 恒空);孤儿检测接通(ORIGIN_PATH 落盘后 supersede 孤儿与源消失可检出)。
119
+ - reasonix 投影漂移改 marker-hash 比对(宿主改写只报告不覆盖);陈旧投影自洽才清理。
120
+ - dedupe 候选排除 deprecated 终态条目;`memory_import` 读取收口 `safe_read`(lstat/fstat 统一防护);safeio 读侧补 lstat↔fd 身份比对。
121
+ - el 契约测试锁定 `list --json` 完整旧 12 字段集;S5 errors>0 拒绝落盘补回归;sweep `--apply` 候选缺失路径先备后写锁。
122
+
123
+ ## [0.1.12] - 2026-09-25
124
+
125
+ ### Added
126
+
127
+ - **KB 锁超时可配置**(`config.py` / `safeio.py`):新增 `safeio.lock_timeout_seconds` 配置键(默认 10s,env `AGENOTE_LOCK_TIMEOUT_SECONDS`,非数值或不大于 0 回落默认),`kb_lock` 显式传参优先于配置值。
128
+ - 回归测试 38 个(全量 214 → 252):BOM title 进索引、重命名回滚双失败保副本、部分安装 extract/reconcile、bash 全局选项后补子命令、orgfmt 失败报告、viz serve 容错、属性值换行拒绝、锁超时配置化、crush 路径覆盖判定等。
129
+
130
+ ### Fixed
131
+
132
+ - **zsh 补全的选项值列表全部失效**(`completions.py`):`_arguments` 的 `:{a,b,c}` 写法会被 zsh 当成 shell 代码执行(实测 `command not found: aa,bb,cc`),`--source` / `--type` / `--theme` / `--domain` 的候选列表静默为空;改为 `:消息:(a b c)` 形式并重新生成 `completions/_agenote`(`test_zsh_completion_uses_separated_values` 同时锁住空格分隔形式)。
133
+ - **带 BOM 的卡片无法被策展**(`orgserde.py`):首行 UTF-8 BOM 会让顶层标题匹配 `^\* ` 失配,`update` / `touch` / `archive` / `restore` / `merge` 一律以 `OrgPropertyDrawerError` 失败(fail-closed,不损坏数据但卡片等于只读)。现在只在标题匹配时剥离首行 BOM,写回仍使用原始行,BOM 字节保持不变。
134
+ - **`inbox-archive` 失败后留下 `ensure_dirs` 骨架文件**(`inbox_archive.py`):事务快照原本在 `ensure_dirs` 之后读取,索引 / inbox 原本不存在时会被补建成空文件,回滚只能恢复到「空骨架」而非「不存在」。快照前移到 `ensure_dirs` 之前,并把 inbox 纳入回滚集(无论是否 `--prune`)。
135
+ - **reconcile 索引写入缺校验、读取放行非法数值**(`reconcile.py`):`trust_score` / `weight` 增加有限性校验(NaN / Infinity 不再落盘或回读),`id` 必须以 `source` 前缀派生;顶层 `version` / `updated` / `by_source` 类型错误一律 fail-closed;`_save_reconcile_index()` 落盘前复用同一 `_valid_reconcile_fact()` 校验。
136
+ - **`reconcile --source all` 留下半更新索引**(`reconcile.py`):非 dry-run 现在先计算所有已注册 source,全部成功后才一次性落盘并清理退役 source;任一 adapter 失败时索引保持不变。全量模式只保留本轮各注册源的新结果,退役 source 计入 `pruned`;单 source reconcile 仍只替换自己,dry-run 不落盘;单源或全量遇到 adapter 返回错误时同样 fail-closed,不覆盖 last-known-good 索引。
137
+ - **退役 source 的公共 CLI 把错误报告成成功**(`extract/base.py` / `reconcile.py` / `cli.py`):`extract` / `reconcile --source hermes` 现在以专用 `UnknownSourceError` 退出 1、错误写 stderr 且不泄漏 traceback;adapter 内部 `ValueError` 不再被 session 级宽泛捕获吞掉。
138
+ - **`parse_org_prop` 忽略正文伪属性**(`orgserde.py` / `core.py`):卡片元数据只读取一级标题后的顶层 `PROPERTIES` 抽屉;resolver 按真实 `:ID:` 精确匹配后回退唯一文件名片段,重复或模糊匹配返回未找到;直接路径、相对路径、符号链接和坏 UTF-8 候选均拒绝,空白/`.`/`..` selector 与 `experiences` 根符号链接也不能绕入其他域。
139
+ - **短 ID 模糊匹配可能误写派生卡片**(`core.py` / `cards.py`):`20260924-111827` 会同时命中原卡和 `20260924-111827-2/3-*` 派生卡,旧 resolver 直接取目录遍历的第一个候选,导致 `update` / `touch` 写错卡。现在先按 Org `:ID:` 精确命中,再回退文件名片段;`get` 复用同一规则,并保留知识库外绝对路径拒绝。
140
+ - **卡片属性写入会命中正文示例**(`orgserde.py` / `core.py` / `cards.py` / `curator.py`):`get --used`、`touch`、`update`、`archive` / `restore` 与 `merge` 现在通过顶层抽屉专用的 `set_org_prop` / `delete_org_prop` 读写,递增 `USAGE_COUNT` 不再改写正文里的 `:LAST_USED:` / `:USAGE_COUNT:` 示例。`get --used` 持 KB 锁,8 路并发不会静默丢计数。
141
+ - **shell 补全只做生成器自洽校验**(`completions.py` / `completions/*`):删除不存在的 `curate` 候选;Bash 的 `memory --type` / `viz --theme` 改回空格分隔候选,补齐 `scan-memories --source`、Zsh 同项与全局 `--domain`;测试现在比较真实 CLI dispatch,并执行 Bash/Fish 补全与静态脚本生成结果。
142
+ - **extract / reconcile 错误路径仍留下半成品**(`extract/base.py` / `reconcile.py` / `index.py` / `core.py` / `orgserde.py`):extract 先完成全部 source 渲染,adapter 失败不发布;发布阶段若第二个文件写入失败,会撤销本轮已写文件。reconcile(包括 dry-run 读取 LKG)严格验证已有索引,损坏索引不覆盖;`get --used` 在写卡片前验证索引形状与真实顶层 `PROPERTIES` 抽屉,失败时不输出正文、不改卡片、CLI 无 traceback。
143
+ - **批量 `merge` 后半程写失败留下部分归档**(`cards.py`):先在内存完成全部卡片变换和索引准备;写阶段失败则恢复所有已写卡片,避免 secondary 已归档而 primary 尚未更新的半完成状态。
144
+ - **损坏的 reconcile 索引被读路径静默跳过**(`reconcile.py`):不仅写路径,公共 `load_reconcile_facts()` 也要求每条事实符合 `ReconciledFact` 的完整序列化结构(`id`、`source`、`native_id`、`title`、`category`、`content`、数值信任权重、`tags` 与时间字段);任一元素非法即 fail-closed,`dream` / `distill` 等消费者不再把半份事实当完整数据。
145
+ - **损坏的卡片索引被公共读路径伪装成空库**(`index.py` / `cards.py` / `curator.py` / `health.py` / `doctor.py` / `distill.py` / `viz/cli.py`):已有 `index.json` 统一校验顶层计数与卡片条目结构,损坏时 `list` / `stats` / `health` / `doctor` / `distill` / `viz` 以 rc 1、可读 stderr、无 traceback 失败;只有索引文件不存在时才返回空骨架。
146
+ - **批量 `merge` 成功时 secondary 索引状态漂移**(`cards.py`):合并时同时 upsert 所有 secondary 与 primary,成功输出延迟到索引提交后;提交失败会恢复卡片、索引原始字节和未误报的 stdout。
147
+ - **批量 `archive` 与 `update --category` 破坏数据完整性**(`core.py` / `cards.py` / `curator.py` / `inbox_archive.py`):归档先解析并准备整批内容,任何 ID 无效或任一写入失败时不留下部分归档,并恢复索引原始字节;add、update 与 inbox archive 复用同一 category 边界校验,路径分隔符和 `..` 一律拒绝。
148
+ - **BOM 卡片 title 全链路解析**(`orgserde.py` / `reconcile.py`):`read_org_title` 与 `_kb_titles` 对首行 BOM 容错,BOM 卡片 `touch` / 策展后 index 与 reconcile 去重不再落到 `unknown`,此前的 BOM 修复从属性抽屉补全到标题链路。
149
+ - **`cmd_update` 重命名回滚可能双删卡片**(`cards.py`):索引写失败且卡片恢复写也失败时,不再删除改名后的新副本(至少一个副本存活),失败明细聚合后仍 fail-loud 抛出。
150
+ - **未安装源打穿 `extract --source all` / `reconcile`**(`extract/*` / `reconcile.py`):源数据不存在降级为 `[skip]` 报告项,不再计为失败、不再阻塞整批落盘;真实错误(坏库、解析失败)仍 fail-closed 整批拒绝。源 0 facts 不再视为错误,清空源数据后可正常更新索引。
151
+ - **bash 补全在全局选项后无法补全子命令**(`completions.py` / `completions/agenote.bash`):`agenote --version <TAB>`、`agenote --domain human <TAB>` 恢复子命令候选;fish 补齐 `reconcile` / `extract` 的 `--dry-run`,zsh `get` 分支补齐 `-h/--help`。
152
+ - **`orgfmt` 失败时吞掉已处理文件的报告**(`orgfmt.py` / `orgfmt_cli.py`):失败退出码保留,但先打印已完成文件的报告与汇总再退出。
153
+ - **`viz --serve` 浏览器打开失败杀死服务器**(`viz/cli.py`):`xdg-open` 失败降级为 stderr 警告,访问 URL 无条件打印,服务器继续运行。
154
+ - **属性值注入防护与错误消息失实**(`orgserde.py` / `core.py` / `cli.py` / `extract/*`):`set_org_prop` 拒绝含换行的值(防伪 `:END:` 抽屉注入),category 校验同步拒绝换行;KB 锁超时的可操作提示完整透传到 stderr;git 失败消息不再虚构异常类型名;claude / codex / omp 未安装消息不再携带完整本地路径;`safe_adapter_error` 对字符串输入不再虚构「Exception」表述;crush 单 session 错误补齐缺失导入。
155
+ - **crush 全局库判定不遵守配置覆盖**(`extract/crush.py`):由 `.config/crush` 字符串包含改为与 `CRUSH_GLOBAL_DB` 配置值路径比对,覆盖 `crush_global_db` 后不再误判为项目库。
156
+ - **extract 发布写盘绕过原子写原语**(`extract/base.py` / `safeio.py`):KB 内输出路径改走 `atomic_write`(tmp→rename),显式 `--output-dir` 的 KB 外路径保留直接写;批级回滚语义不变。
157
+
158
+ ### Removed
159
+
160
+ - **退役 Hermes 旧 SQLite 记忆源的 extract/reconcile 管线**(旧 `extract/hermes.py`、`hermes_db` 与 `hermes_weight_cap` 配置项):六个对话源继续由 adapter registry 管理;Hermes 的内置 `MEMORY.md` / `USER.md` 仍由只读 `scan-memories --source hermes` 单独扫描。历史 CHANGELOG/ADR 语境保留。
161
+
162
+ ## [0.1.11] - 2026-09-22
163
+
164
+ ### Changed
165
+
166
+ - **`KNOWN_AGENTS` 硬打表改为动态收录**(`core.py` / `index.py` / `cards.py`):写死的 9 项 agent 白名单删掉,与 type 门禁(SEED_TYPES/formal_types)同构——已知 agent = `SEED_AGENTS` 种子集 ∪ index 中出现过的 `source_agent`,实时计算零持久状态。新 agent(如 dsh)首张卡给提示性警告(注明写入后自动收录),第二张起静默;无需为接入新 agent 改代码。
167
+ - **completions 枚举全部改为从真相源派生**(`completions.py`):`CONFIG_SOURCES`/`MEMSCAN_SOURCES`/`TYPE_VALUES`/`OWNER_VALUES`/`ENTRY_VALUES`/`STATUS_VALUES` 等手抄第二份删掉,fish/bash/zsh 生成器内的内联字符串改为运行时从 extract registry、memscan registry、`core` 领域枚举拼装——新增 extract adapter 或领域枚举调整自动反映到三个 shell 的补全,不再出现补全与实际 choices 漂移。
168
+ - **`MEMORY_SECTIONS` 收敛为 `MEMORY_TYPES` 派生**(`core.py`):`feedback/project/reference` 只在 `MEMORY_TYPES` 声明一次,`MEMORY_SECTIONS = [*MEMORY_TYPES, "deprecated"]`(deprecated 是生命周期终态语义特例)。
169
+
170
+ ### Added
171
+
172
+ - 回归测试两套:`test_agent_registry.py`(首卡警告→收录→次卡静默、空 KB 种子集、归档卡写入者不失联)、`test_completions_derived.py`(枚举跟随真相源、生成脚本携带派生值)。全量 130 passed。
173
+
174
+ ## [0.1.10] - 2026-09-14
175
+
176
+ ### Fixed
177
+
178
+ - **hermes 源记忆库按日整份重复导出**(`extract/hermes.py`):hermes facts 不填 `timestamp`,而日期过滤对空时间戳「不过滤」(防静默丢数据),117 条记忆在 14 天产物里各出现一次。改为取 DB 的 `updated_at`(改写即算当天浮现)、缺值回落 `created_at`;实测 09-13 从 117 条降到 1 条,14 天语料 1.9MB → 0.85MB。
179
+ - **`list --unused-days` 把复核过的老卡列回候选**(`cards.py`):判定只看 `last_used`,转 `stable` 后仍被列出,同一批老卡每轮策展重审一遍。候选改为只收 `status=done`(`stable` 已是复核终态)。
180
+ - **`memory --stale` 报出已归档条目**(`memory.py`):`deprecated` 节下的条目仍按 `UPDATED` 日期出现在陈旧清单里。改为跟踪一级节并跳过 `deprecated`。
181
+
182
+ ## [0.1.9] - 2026-09-14
183
+
184
+ ### Fixed
185
+
186
+ - **`scan-memories` 在 pi 源崩溃**(`config.py`):`memories.sources` 键名 `pi_agent_dir` 与 `resolve_xdg_path` 的查表口径(`env.lower()` = `pi_coding_agent_dir`)不一致,`PI_CODING_AGENT_DIR` 未设时(日常终端)直接 `KeyError`。键名对齐 SCHEMA 注释声明的「键名 = env 小写」,并补两项回归测试:六源 env 与 SCHEMA 键一致性、env 未设走配置/XDG 分支。
187
+ - **lint 误报 fingerprint 字段数**(`lint.py` / `core.py`):构建器在 tech 与 category 相同、entry 为空时按设计省略对应段,lint 却按固定 5 段判定,把合规卡片成片报成问题。现将 fingerprint 收敛为单一真相源 `core.build_fingerprint_line`,add 组装与 lint 校验共用,lint 改为逐字比对期望值。
188
+ - **`search` 裸多关键词报错**(`cli.py` / `search.py`):位置参数改 `nargs="+"`,`agenote search a b` 不再报 `unrecognized arguments`,与 `--help` 的 `<关键词...>` 一致;多词在 `cmd_search` 内合并回单串,下游拆分逻辑不变。
189
+
190
+ ### Changed
191
+
192
+ - **extract 落盘前过滤 harness 元消息**(`extract/base.py` / `core.py`):复用 reconcile 的 `is_noise_fact`(口径统一),剔除 TodoWrite 提醒、`<task-notification>`、`<system-reminder>` 等模板消息;`NOISE_MARKERS` 补 `<task-notification>` / `<subagent-message>` / `[OUT-OF-BAND USER MESSAGE` / `Continuing toward your standing goal` 四类此前漏检的标记。实测单日 zcode 4323 条、opencode 1016 条被剔除,报告新增 `noise_filtered` 计数。
193
+
194
+ ## [0.1.8] - 2026-09-12
195
+
196
+ ### Added
197
+
198
+ - **type 正式性动态晋升**(`cli.py`):移除静态 type 白名单。正式 type = 种子 6 类 ∪ 非归档卡片数达晋升阈值(`curation.type_promote_min`,默认 10)的 type;门禁按同一口径实时计算——非正式 type 写入(`add`/`update --type`)被拒绝、`--force` 逃生,持续写入满阈值后自动转正免检。
199
+
200
+ ### Fixed
201
+
202
+ - **`update --tech` 同步标签与索引**(`cards.py`):`update` 改 `--tech` / `--category` / `--owner` 后按属性重建 `:END:` 后的 fingerprint 标签行并刷新索引,修复属性与 `tags` / `agenote tags` / `fields` 漂移(此前仅 `--type` 会同步)。供策展 tech 聚拢流程使用;`--status` 保持轻量(仍由 `reindex` 刷新)。
203
+
204
+ ## [0.1.7] - 2026-08-29
205
+
206
+ 策展分工定型(ADR-0004):CLI 收敛为「只读候选发现 + 原子写命令」两层,策展流程改由 agent 依据 agenote-curator skill 主导。
207
+
208
+ ### Added
209
+
210
+ - **type 写入门禁**(`cli.py`):`add` / `update` 对知识库中不存在的新 type 硬拒绝并提示已有合法集合,`--force` 逃生;替代原软警告(被各 agent 无视,非标准 type 持续增殖)。`update --type` 由仅改 `:TYPE:` 属性升级为完整重分类,同步标签行、文件名与索引,供策展 type 聚拢流程使用。
211
+ - **`scan-memories` 子命令**(`memscan`):只读扫描 zcode / claude / codex / pi / reasonix / hermes 六源持久记忆文件,输出条目清单供策展 agent 审查后显式导入,不自动写 KB;根目录解析复用 `resolve_xdg_path`(env > config.toml > XDG > ~/),`resolve_xdg_path` 增加 section 参数接入 `memories.sources` 配置节。
212
+ - `list --json` compact 输出扩展 `status` / `last_used` / `usage_count` / `source_agent` / `file` 五字段(均为索引已有数据),供 agenote-el 总览界面做状态筛选、作者列与直开卡片文件等策展决策。
213
+ - `list --unused-days N`:降级候选的只读发现入口;`archive` 支持批量 id。
214
+
215
+ ### Changed
216
+
217
+ - **BREAKING**:移除 `curate` 一键策展编排及全部自动写盘路径,策展决策(状态转换、去留、权重取舍)交由 agent 显式执行。连带移除:`review --fix`、`deduplicate --merge`、`memory --auto-archive-days` / `--auto-update`、`search --regex`、`list --cagetory`、`dream` / `distill` 的 `--dry-run`;`archive --stale` 反转为只读归档候选清单;WEIGHT 改为派生值(按 usage/新鲜度公式重算),`add` 不再写文件层 WEIGHT 属性;shim 仅保留 `health`。
218
+ - 决策记录 ADR-0004(含被拒方案:机械阶段一键 / `--apply` / 独立 reweight 命令)并同步 CONTEXT.md 术语(curate / distill / 候选清单)。
219
+
220
+ ### Fixed
221
+
222
+ - omp 会话目录解析优先读 `PI_CODING_AGENT_SESSION_DIR`(顺序:`OMP_SESSIONS_DIR` > `PI_CODING_AGENT_SESSION_DIR` > config.toml > 默认值),修复 omp 18 迁移后 `reconcile` 的 omp 源一直抽不到事实的问题。
223
+
224
+ ## [0.1.6] - 2026-08-24
225
+
226
+ 核心设计移植自 [claude-obsidian](https://github.com/AgriciDaniel/claude-obsidian)(写入安全 / BM25 检索 / doctor 能力检测 / lint 分类报告,详见 README 致谢)。
227
+
228
+ ### Added
229
+
230
+ - **写入安全层**(`safeio.py`):变更类子命令统一持 flock 全局 KB 锁(`cli.py` 单点包锁,锁文件 `.agenote.lock`);KB 内数据文件全部改走原子写(tmp + rename,containment 校验仅放行 KB_ROOT 内);`add` 同秒并发撞车自动追加 ID 序号后缀。修复多 agent 并发 `add` 互相覆盖、index.json read-modify-write 丢条目、写一半崩溃留半文件三类缺陷。
231
+ - **BM25 检索**(`ranking.py`):检索打分从子串计数升级为纯 stdlib Okapi BM25(k1/b 可配),CJK 1/2/3-gram 分词支持中英混检;两域卡片 + MEMORY + reconcile 事实统一进语料算 IDF,域权重/标题加成/短语加成/all-terms/snippet 全部保留。
232
+ - `doctor` 子命令:环境自诊断(外部工具 PATH 探测 + 缺失降级说明 + verification_reason 惯例;config.toml 未知键复用加载警告同一口径;两域 index 与磁盘卡片数一致性检查)。`--json` 供 agent 消费。
233
+ - `lint --json`:分类结构化报告(`summary.category_counts` + 条目 `{file, reason}`,分类键 format/missing_entry_type/enum_drift/fingerprint/missing_sections),修复前后可差分对比。
234
+
235
+ ### Changed
236
+
237
+ - **BREAKING**:检索 `score`/`raw_score` 数值尺度变化(BM25 分),排序质量单调提升(稀有词与高频词有了 IDF 区分度)。
238
+ - **BREAKING**:配置 `weights` 节删除 `score_title_bonus` / `score_phrase_bonus`(改用 `[search]` 的 `title_boost` / `phrase_boost`,Okapi 尺度);`score_term_hit` 保留但仅用于命中块展示排序。旧 config.toml 残留键会触发未知键警告(无害)。
239
+ - KB 侧 `.gitignore` 模板新增 `.agenote.lock` 条目(`init` 同步)。
240
+
241
+ ## [0.1.5] - 2026-08-20
242
+
243
+ ### Added
244
+
245
+ - 配置文件支持:`~/.config/agenote/config.toml`(TOML,遵循 XDG),84 个配置键覆盖知识库路径(`kb_root`、agent 域目录、各产物目录)、7 个 extract 源数据库路径、检索权重、策展阈值、dream/distill 启发式等;优先级为环境变量 > 配置文件 > 内置默认值,CLI 显式传参最优先。
246
+ - 新增 `agenote config` 子命令:`config init` 生成带注释的配置模板,`config show` 打印当前生效配置及来源(env / file / default)。
247
+ - GitHub Actions CI:push / PR 自动运行 pytest(Python 3.10–3.12 矩阵)。
248
+ - CHANGELOG.md 与 CONTRIBUTING.md(开发环境、Conventional Commits 规范)。
249
+
250
+ ### Changed
251
+
252
+ - CLI 生成与建议的知识库 commit message 对齐 Conventional Commits:`agenote init` 初始提交改为 `chore(init): 初始化知识库`,策展提交建议改为 `chore(curate): …` / `feat(card): …` 格式。
253
+ - 全部模块常量接入配置层并收敛双源常量:去重阈值(4 处)、去重加权(2 份)、默认权重(viz 副本)、add 默认值(cards/index 两份)单源化;修复 KB_ROOT 三处独立硬编码不随配置变化的问题;hermes 源获得与其他 6 源一致的 env/config 覆盖链。
254
+
255
+ ### Fixed
256
+
257
+ - `AGENOTE_AGENT` 设为纯空白时不再把空白串写入 SOURCE_AGENT(回落默认值)。
258
+ - 配置值类型错误时报键名退出,不再裸 traceback。
259
+ - lint `--fix` 写回前路径归一化并限定 `.org` 文件;viz serve 探测限定 localhost;HTML 骨架模板占位符白名单(静态审计加固)。
260
+
261
+ ## [0.1.5.1] - 2026-08-20
262
+
263
+ ### Added
264
+
265
+ - fish / zsh / bash 自动补全:`agenote completions <shell>` 动态生成,`completions/` 静态脚本随仓库分发(`agenote.fish` / `_agenote` / `agenote.bash`),覆盖全部 34 个子命令与常用枚举(`--domain` / `--type` / `--source` 等);`completions.py` 为单一真相源,`completions/` 与动态输出逐字节一致。
266
+
267
+ ## [0.1.2] - 2026-08-03
268
+
269
+ ### Changed
270
+
271
+ - jieba 改为硬依赖,确保 `dream` 子命令开箱即用。
272
+ - 重构为 PEP 621 src layout + uv 打包。
273
+
274
+ ### Removed
275
+
276
+ - 移除 `ag-ent`、`agenote-mcp`(MCP 层由 agent skills 仓库承担)。
277
+
278
+ ## [0.1.0] - 2026-07-05
279
+
280
+ ### Added
281
+
282
+ - 首个发布版本:卡片 CRUD、检索、记忆系统、策展、跨 agent reconcile / dream / distill、7 源对话抽取、HTML 可视化。更早的逐条变更见 git log。
agenote-0.2.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BrokenShine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
agenote-0.2.1/PKG-INFO ADDED
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.5
2
+ Name: agenote
3
+ Version: 0.2.1
4
+ Summary: 跨 Agent 经验平台 CLI — 知识卡片、记忆、策展与工作流蒸馏
5
+ Project-URL: Homepage, https://github.com/ShineBreaker/agenote
6
+ Project-URL: Repository, https://github.com/ShineBreaker/agenote
7
+ Author-email: BrokenShine <xchai404@gmail.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: agent,cli,experience,knowledge-base,notes
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: jieba
24
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
25
+ Provides-Extra: test
26
+ Requires-Dist: pytest>=8; extra == 'test'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # agenote: cross-agent knowledge base
30
+
31
+ [![CI](https://github.com/ShineBreaker/agenote/actions/workflows/ci.yml/badge.svg)](https://github.com/ShineBreaker/agenote/actions/workflows/ci.yml)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
33
+
34
+ > One memory, six agents sharing it.
35
+
36
+ Agents don't share memory. A pitfall you hit in Claude Code has to be hit again in
37
+ Codex, because every host stores its memory in a private format you can neither read nor
38
+ maintain. agenote collects those memories into one Org directory that both AI and humans
39
+ read and write as the same files.
40
+
41
+ ## Why Org rather than Markdown
42
+
43
+ Most memory systems are built for the Markdown ecosystem: `.md` files, paragraphs for
44
+ body text, heading levels for structure, front matter for state. agenote takes the other
45
+ route, and every card is a standard org file.
46
+
47
+ This isn't a wrapper. Card structure uses org's native mechanisms directly:
48
+
49
+ - **Property drawers** hold structured metadata. `CATEGORY`, `TECH`, `STATUS`, `WEIGHT`,
50
+ and `USAGE_COUNT` are drawer keys, not lines in the body.
51
+ - **TODO keywords** carry the state machine. The heading is always `* DONE`, with the
52
+ real state in the `:STATUS:` property, and the CLI moves cards through four states
53
+ (done, stable, stale, archived).
54
+ - **Sections and inline markers** keep org syntax. `** 场景` is an org subsection,
55
+ `~orgfmt --check~` is an org code marker rather than a backtick.
56
+
57
+ You get three things a Markdown design can't offer:
58
+
59
+ **Humans can edit directly.** Open Emacs and `M-x org agenda` lists the cards by state;
60
+ `org-refile` moves them; `[[card][description]]` links are clickable. Maintaining the
61
+ knowledge base needs no agent and no separate interface.
62
+
63
+ **Zero learning cost for Emacs users.** If you already take notes in org mode, recording
64
+ an agent's lessons is just more entries in the same drawers.
65
+
66
+ **Readable Git diffs.** Which sentence changed in a card is visible at a glance in org.
67
+ Large Markdown files are mostly reflowing in a diff.
68
+
69
+ The cost is explicit: it assumes you have Emacs or at least an editor that understands
70
+ org syntax. Even without one, the files are plain text and nothing is lost, you just
71
+ lose agenda and refile.
72
+
73
+ ## What it stores
74
+
75
+ Two kinds of content, with a clear boundary:
76
+
77
+ - **Experience cards** in `experiences/`. One reusable lesson each, with a title, category,
78
+ tech stack, body, and status. This is the knowledge base's atom.
79
+ - **Memory entries** in `MEMORY.org`. Long-lived constraints that cut across cards, such as
80
+ your preferences, a project's build commands, or gotchas specific to one machine. Lighter
81
+ than cards, and injected into sessions first when you search.
82
+
83
+ Cards you write by hand and cards written by agents live in separate subdirectories and
84
+ don't contaminate each other. Search spans both domains by default, weighting what you
85
+ wrote higher.
86
+
87
+ ## Core capabilities
88
+
89
+ **Write safety.** Mutating commands hold a flock process lock, writes go through tmp +
90
+ rename atomically, and same-second writes get an ID suffix appended. Several agents writing
91
+ to one knowledge base don't collide.
92
+
93
+ **Mixed Chinese-English search.** BM25 ranking with CJK 1/2/3-gram tokenization, so an
94
+ English command name embedded in a Chinese sentence still matches.
95
+
96
+ **Write gating.** Secret scanning is on by default and rejects key-shaped content. Sensitive
97
+ memory entries carry a marker and stay local, never injected into a session or projected to
98
+ a host.
99
+
100
+ **Trace back to the source.** Every experience links back to the original full conversation,
101
+ tool calls and reasoning included, rather than to a summary.
102
+
103
+ **A curation loop.** Health reports, dedup, status downgrade, archive, and weight recompute
104
+ are all atomic commands. The agent decides what to keep based on the spec; the CLI only
105
+ surfaces candidates and the evidence behind them.
106
+
107
+ **Visualization.** `agenote viz` renders the whole knowledge base into a single searchable
108
+ HTML file.
109
+
110
+ ## Six hosts
111
+
112
+ zcode, Claude Code, Codex, oh-my-pi, opencode, and Hermes each have a plugin or injector
113
+ wired to `agenote context`. A brief is injected at session start, and task completion
114
+ triggers experience capture, with the exact behavior defined by shared skills. Each host's
115
+ built-in memory write side must be turned off by hand; `agenote doctor` checks them one by
116
+ one.
117
+
118
+ The knowledge base itself is a set of org files, and a Python CLI handles cards, search,
119
+ curation, and health. 37 subcommands, 460 tests, Python ≥ 3.10, no database.
120
+
121
+ ## Versus built-in host memory
122
+
123
+ | Dimension | Built-in host memory | agenote |
124
+ | --- | --- | --- |
125
+ | Scope | Private to one host | Six hosts share one knowledge base |
126
+ | Storage | Per-host private formats (Markdown fragments, SQLite, JSONL) | Unified org files, human-readable |
127
+ | Write side | Each host writes independently | Host writes disabled; the CLI is the sole writer |
128
+ | Search | Local, per-host matching | Global BM25 with CJK n-gram |
129
+ | Curation | None | Health, dedup, downgrade, archive, weight recompute |
130
+ | Visualization | None | HTML visualization and an Emacs dashboard panel |
131
+
132
+ ## Quick start
133
+
134
+ ```bash
135
+ uv tool install git+https://github.com/ShineBreaker/agenote.git
136
+
137
+ agenote init # set up the knowledge base, default ~/Documents/Org
138
+
139
+ agenote add --title "concurrent write test" --category testing --tech Python
140
+ ```
141
+
142
+ The full command set, configuration, and architecture are in the
143
+ [usage guide](docs/usage.md).
144
+
145
+ ## Who shouldn't use this
146
+
147
+ - One person, one agent. Host built-in memory is enough.
148
+ - You already have a note system that works and don't intend to let AI read or write it.
149
+ - You need team sharing, multi-user permissions, or real-time collaboration. agenote is a
150
+ single-machine file store versioned with Git.
151
+
152
+ ## Ecosystem
153
+
154
+ - [agenote-skills](https://github.com/ShineBreaker/agenote/tree/main/packages/agenote-skills): three agent skills (base, curator, review)
155
+ - [agenote-pi](https://github.com/ShineBreaker/agenote/tree/main/packages/agenote-pi): the oh-my-pi integration extension
156
+ - [injectors/](injectors/README.md): memory injectors for the six hosts
157
+ - [agenote-el](https://github.com/ShineBreaker/agenote/tree/main/packages/agenote-el): the Emacs integration package
158
+
159
+ ## Read more
160
+
161
+ - [Usage guide](docs/usage.md): installation, command reference, configuration, architecture, development
162
+ - [中文 README](README.zh.md)
163
+ - [Contributing](https://github.com/ShineBreaker/agenote/blob/main/CONTRIBUTING.md) · [Changelog](CHANGELOG.md) · [Architecture decisions](docs/adr/)
164
+ - [Glossary](https://github.com/ShineBreaker/agenote/blob/main/CONTEXT.md): what cards, memory entries, and reconcile actually mean
165
+
166
+ ## License
167
+
168
+ MIT, see [LICENSE](LICENSE).