dev-memory-cli 0.18.2 → 0.19.2

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/README.md CHANGED
@@ -6,18 +6,19 @@
6
6
 
7
7
  ![套件总览](docs/diagrams/overview.png)
8
8
 
9
- ## v2 架构:4 个 Skill + branch CLI
9
+ ## v2 架构:5 个 Skill + branch CLI
10
10
 
11
- v2 把旧的 sync + update 合并成统一的 capture,并把 setup 从前置门禁改为 merge 动作、加 lazy init、加 v1→v2 自动迁移。0.16 起取消 `using-dev-memory` 路由总入口;0.17 起再取消 `dev-memory-context` 读取 skill —— SessionStart hook 注入末尾直接列出权威记忆文件的绝对路径,AI 需要详情时 Read 对应文件即可,不再需要 skill 中转。当前套件 4 skill(全部偏写入 / 流程 / 整理)。
11
+ v2 把旧的 sync + update 合并成统一的 capture,并把 setup 从前置门禁改为 merge 动作、加 lazy init、加 v1→v2 自动迁移。0.16 起取消 `using-dev-memory` 路由总入口;0.17 起取消旧 `dev-memory-context` 读取 skill;现在补回更窄的 `dev-memory-read` 主动读取入口:它只负责精确定位当前 repo+branch 的权威记忆文件和关键词检索,不做 context 注入,也不写入。当前套件 5 skill(1 个读取入口 + 4 个写入 / 流程 / 整理入口)。
12
12
 
13
13
  | Skill | 定位 | 典型触发 |
14
14
  | --- | --- | --- |
15
+ | `dev-memory-read` | **主动读取入口**:定位当前 repo+branch 记忆目录、关键词搜索、返回可直接 Read 的文件路径 | 用户说"重新读一下记忆 / 之前记的 todo / 按记忆最新口径",或 agent 需要主动查记忆 |
15
16
  | `dev-memory-capture` | **统一写入入口**(合并 sync + update) | 本轮产生稳定结论 / checkpoint / 用户手动记一笔 / 改写旧条目 |
16
17
  | `dev-memory-setup` | 整理 unsorted.md + 补元信息 + 标 setup_completed(不再是前置门禁) | unsorted.md 累积、用户明确说"整理一下" |
17
18
  | `dev-memory-tidy` | **定期校准入口**:已结构化条目漂移时(陈旧 / 重复 / 模板残留),agent 聚合 proposal → 浏览器审 → apply 落盘 + 自动备份 | 用户说"整理一下记忆 / 清下过期的 / 看看哪些还成立" |
18
19
  | `dev-memory-graduate` | 分支收尾:从 pending-promotion 提炼上提 + 归档 branch | 用户显式说"归档 / 分支收尾 / merge 完清一下" |
19
20
 
20
- > 读取路径:SessionStart hook 自动注入 progress / risks / decisions 等摘要 + 完整文件路径;后续直接 Read 文件,或跑 CLI `dev-memory-cli context show` / `context sync` paths / 重新刷新(命令保留,只是不再以 skill 形式暴露)。
21
+ > 读取路径:SessionStart hook 自动注入 progress / risks / decisions 等摘要 + 完整文件路径;用户或 agent 主动要求重新查记忆时,走 `dev-memory-read` / `dev-memory-cli read show|search`。`context show` / `context sync` 仍保留给 hook 和脚本场景。
21
22
 
22
23
  详细设计与语义见:
23
24
 
@@ -28,6 +29,17 @@ v2 把旧的 sync + update 合并成统一的 capture,并把 setup 从前置
28
29
 
29
30
  v2 里 capture 写入永远先 lazy init(骨架不存在就自动建),不再需要前置 setup。setup 的新职责:扫 `unsorted.md` 把未分类条目按用户选择 merge 到 decisions/progress/risks/glossary,再标 `manifest.setup_completed = true`。setup 之前 / 之后的区别是 capture 的 heuristic 兜底策略:之前"不确定 → unsorted",之后"不确定 → progress"。
30
31
 
32
+ ### 主动读取:dev-memory-read
33
+
34
+ `dev-memory-read` 是只读 skill,避免 agent 在错误目录里盲搜。它不 lazy-init,不迁移,不写任何记忆文件。
35
+
36
+ ```bash
37
+ dev-memory-cli read show --repo <repo-path>
38
+ dev-memory-cli read search --repo <repo-path> --query "作者信息" --query "头像"
39
+ ```
40
+
41
+ `read show` 输出 `repo_dir`、`branch_dir`、`recommended_read_order`、`existing_branches`;`read search` 默认只查当前 branch + repo 共享层,需要扩大范围时再显式传 `--scope all-branches` 或 `--scope archived`。这能覆盖“当前分支记忆没命中,但旧分支/归档里可能有之前 TODO”的场景,同时不会扫描整个 `~/.dev-memory`。
42
+
31
43
  ### Capture 的写入路由
32
44
 
33
45
  ![capture 写入流程](docs/diagrams/capture.png)
@@ -53,10 +65,11 @@ append 类 kind 写入前会对目标 section 做相似度查重,阻止 append
53
65
  - **行为**:命中拦下不写,`exit 2`,stdout 返 `dedup_hint` 含 `matches[]` + `recommendation`(`update_existing` / `review_and_decide`)
54
66
  - **agent 处理**:按 recommendation 分流:
55
67
  - 修订旧条目 → `dev-memory-cli capture rewrite-entry --id <match_id> --content <text>`(新 subcommand)
68
+ - 删除旧条目 → `dev-memory-cli capture delete-entry --id <match_id>`
56
69
  - 确实是独立新事实 → `dev-memory-cli capture record --force ...`(绕过查重)
57
70
  - 误判同义 / 暂不写 → 不调任何命令
58
71
  - **绕过**:`--force` flag;upsert 类 kind(progress / next / overview / scope / stage / constraint)天然跳过 dedup
59
- - **批量**:`--summary-json` 每条独立 check,blocked 项进 stdout `dedup_blocked[]`,未 blocked 部分照常写
72
+ - **批量**:`record --summary-json` 每条独立 check,blocked 项进 stdout `dedup_blocked[]`,未 blocked 部分照常写;SessionEnd 自动总结优先走 `capture apply-summary-output --json`,支持 upsert / append / rewrite / delete 的结构化 patch
60
73
  - **调试 / 实验**:可用 `--dedup-threshold <float>`(隐藏参数,范围 (0.0, 1.0])临时覆盖默认 0.7 阈值,生产场景不建议改
61
74
 
62
75
  ### Setup vs Tidy vs Graduate(三个整理动作的边界)
@@ -75,6 +88,8 @@ append 类 kind 写入前会对目标 section 做相似度查重,阻止 append
75
88
  - 新 action `delete-block`:把 top bullet + 缩进子树 + 紧跟的 `**Why:**` / `**How to apply:**` bold paragraph 聚成一个语义单元整体删,自动吸附 orphan paragraph
76
89
  - action 优先级 `reset-file > delete-section > delete-block > delete-entries / edit-entries`,apply 时高优先级覆盖低优先级
77
90
  - `delete-block` 的 block_id 在 apply 时**重新解析**当前文件结构定位(不依赖 prepare 时的快照),文件中途被改不误删邻块
91
+ - `prepare` 默认跑两个轻量启发式 pass 给 entry 贴 hint,让 review 从"全表扫一遍"变成"先看高亮":**STALE**(文件在 `log.md` 最近一次出现 ≥ `--stale-after-days`(默认 30)天前 → 该文件下所有 entry 标 STALE)、**ORPHAN**(glossary entry 的 key phrase——冒号前 ≥ 4 字符——在其他 `.md` 零引用 → 标 ORPHAN,同 entry 上 ORPHAN 优先 STALE)。用户传 `--hints-json` / `--hints-file` 时用户优先;`--no-auto-hints` 关闭;冷启动 `log.md` 为空时不标,避免一次性给所有文件贴 STALE(HUB GAP 不实现,中文 NLP false positive 太干扰)
92
+ - `apply` 落盘后写一行 `log.md`(accepted / rewritten / backup 等详情)
78
93
 
79
94
  ### 分支记忆生命周期(0.14 起新增 CLI)
80
95
 
@@ -113,10 +128,22 @@ dev-memory-cli branch inherit-worktree-base [--source NAME] [--backup | --force]
113
128
  - 环境变量 `DEV_MEMORY_DISABLE_WORKTREE_INHERIT=1` 全局关闭
114
129
  - 已经开过会话的 worktree 想后补 → 显式跑 `dev-memory-cli branch inherit-worktree-base`(支持 `--source` 覆盖 reflog 探测)
115
130
 
131
+ 如果希望 worktree 分支写入时把新增知识也写回源分支,可以显式开启 write-back:
132
+
133
+ ```bash
134
+ git config --local dev-memory.worktreeWriteback true
135
+ # 或仅当前进程启用
136
+ DEV_MEMORY_WORKTREE_WRITEBACK=1 dev-memory-cli capture record ...
137
+ ```
138
+
139
+ write-back 只镜像 append 型分支知识:`decision` / `risk` / `glossary` / `source`。`progress` / `next` / `overview` / `scope` / `stage` / `constraint` 这类快照字段不会回源,因为它们表达当前 worktree 分支状态,双写会覆盖源分支自己的工作态。源分支重复内容会走同一套 dedup 检查;被拦截时当前分支写入仍然保留,只在输出的 `worktree_writeback.skipped` 里报告。
140
+
116
141
  ### Graduate 为什么必须显式
117
142
 
118
143
  `dev-memory-graduate` 会做 destructive move(把 `branches/<key>/` 搬到 `branches/_archived/<key>__<date>/`),同时把 branch 记忆里跨分支可复用的知识(剥离业务命名后)上提到 repo 共享层。**只接受用户显式触发**,不做 implicit 调用。在 no-git 模式下直接拒绝(没有分支概念)。Tidy 同样要求显式触发(用户主动说"整理"),不做 implicit。
119
144
 
145
+ apply 写入受 **repo 级队列锁**保护:多个会话可连续发起归档,但 harvest / schema / branch 等 pre-flight 校验先于锁执行(失败立即返回),只有写 repo 共享层和移动归档目录的阶段按 repo memory 目录串行等待;等待者拿到锁后会重新确认 `branch_dir` 仍存在,避免并发写共享记忆或重复 move。
146
+
120
147
  ## 运行模式
121
148
 
122
149
  套件会根据当前工作目录自动切换运行模式,存储布局 key 始终是 `(仓库身份, 分支)`:
@@ -141,14 +168,16 @@ dev-memory-cli branch inherit-worktree-base [--source NAME] [--backup | --force]
141
168
  overview.md # 长期目标 + 约束
142
169
  decisions.md # 跨分支通用决策
143
170
  glossary.md # 共享入口 + 长期背景
171
+ log.md # 仓库事件日志(graduate / shared-* capture 镜像)
144
172
  manifest.json
145
173
  branches/
146
- <branch>/ # 当前分支层(v2 八件套)
174
+ <branch>/ # 当前分支层
147
175
  overview.md # 冷启动摘要(snapshot 型)
148
176
  progress.md # 当前进展 + 下一步 + 自动同步区(snapshot 型)
149
177
  decisions.md # 稳定决策 + Why + 影响(accumulation 型)
150
178
  risks.md # 阻塞 + 注意点(accumulation 型)
151
179
  glossary.md # 术语 + 源资料入口(accumulation 型)
180
+ log.md # append-only 事件时间线(capture / graduate 落盘后追加)
152
181
  unsorted.md # heuristic 兜底(setup 时分类)
153
182
  pending-promotion.md # 跨分支候选 staging(graduate 预筛源)
154
183
  manifest.json # 含 setup_completed
@@ -165,6 +194,7 @@ dev-memory-cli branch inherit-worktree-base [--source NAME] [--backup | --force]
165
194
 
166
195
  **其他关键点:**
167
196
 
197
+ - `log.md`:append-only 事件时间线,capture record / rewrite-entry、graduate apply、tidy apply 落盘后各追加一行 `## [ISO] action · kind | summary`,`grep '^## \[' log.md | tail -20` 看最近事件。SessionStart 只列文件路径不抽正文(lazy loading);`sync-working-tree` / `record-head` 故意不写以免机器噪音冲洗日志;`shared-*` kind 写入会镜像到 repo log,分支-only 写入不污染 repo log
168
198
  - `repo-key`:优先按仓库 remote 身份派生,不只看目录名;支持多 clone / worktree 共享同一套记忆
169
199
  - `DEV_ASSETS_ROOT`:覆盖默认 `~/.dev-memory/repos`;CLI、所有 hook 脚本、所有 skill 脚本都尊重此环境变量
170
200
  - v1 → v2 迁移在第一次 capture/context 时自动触发,老的 `development.md` / `context.md` / `sources.md` 按 section 切分进 v2 对应文件后删除(单用户离线清理,不保留 .legacy)
@@ -252,16 +282,20 @@ dev-memory-cli ui --read-only # 禁用编辑回写
252
282
 
253
283
  | 事件 | Claude | Codex | 做什么 |
254
284
  | --- | :-: | :-: | --- |
255
- | `SessionStart` | ✅ | ✅ | 跑 `context sync` 刷 progress.md auto 区,抽 14 段摘要 + 列权威记忆文件路径,注入会话上下文 |
285
+ | `SessionStart` | ✅ | ✅ | 跑 `context sync` 刷 progress.md auto 区,抽 14 段摘要 + 列权威记忆文件路径,注入会话上下文;同一 session resume 重触发时只写日志并返回空 payload |
256
286
  | `PreCompact` | ✅ | ✕ | **0.17 起 no-op**(SessionStart 已经刷过,重复跑无信号) |
257
287
  | `Stop` | ✅ | ✅ | 每次回复后落一个轻量 HEAD marker |
258
- | `SessionEnd` | ✅ | ✕ | 会话结束时再落一次最终 HEAD |
288
+ | `SessionEnd` | ✅ | ✕ | 会话结束时再落一次最终 HEAD,并把 transcript 总结任务写入队列 |
259
289
 
260
290
  重要边界:
261
291
 
262
292
  - 本仓库只提供**模板 + CLI**,真正生效的是你本地 `.codex/hooks.json` / `.claude/settings.local.json` / `~/.codex/hooks.json` / `~/.claude/settings.json` 里有没有合并进来
263
293
  - hook 运行时统一走 `dev-memory-cli hook ...`,所以 CLI 必须在 PATH 上或可被 `npx` 解析
264
294
  - hook 只做**低摩擦恢复 + 轻量刷新**,不在 hook 里重写高语义正文
295
+ - `SessionStart` 以 `<repo-memory>/jobs/session-start/injected/*.json` 做同 session 幂等;Codex resume 等场景再次触发生命周期事件时,只写 skip 日志并返回空 payload,不重复注入上下文
296
+ - `SessionEnd` 只 enqueue 总结任务,不同步跑总结:任务写到 `<repo-memory>/jobs/session-summary/pending/*.json`,事件日志写到同目录 `events.jsonl`。同一 repo+branch+session 会更新同一个 job,避免短时间重复结束时冲突
297
+ - enqueue 后是否启动后台 summarizer 由 `~/.dev-memory/config.json` 的 `session_summary.command` 决定;`install-hooks` 会扫描本地工具,按 `coco -> codex -> claude` 顺序写默认值。coco 默认用 `--session-id {summary_session_id}`,Claude 默认用 UUID 形态的 `{summary_session_uuid}`。hook 会启动后台 worker,worker 先确定性提取 transcript core messages + existing memory,写入 `<repo-memory>/jobs/session-summary/inputs/*.json`,再把这份 JSON 内联进 `{prompt}`;agent 只输出 summary-output JSON,不调用 CLI、不移动 job。worker 会校验 JSON,格式错误时用同一个 summary session 最多重试 3 次,然后由代码执行 `apply-summary-output` 并迁移 pending/done/skipped/failed;无 touched targets 的空总结进入 `skipped/`,不刷新 capture manifest,不算真实落盘
298
+ - `DEV_MEMORY_SESSION_SUMMARY_CMD` 只作为临时调试 override;要禁用后台 summarizer,可清掉配置里的 `session_summary.command`,或设置 `DEV_MEMORY_DISABLE_SESSION_SUMMARY_AGENT=1`
265
299
  - 全局 skill 安装不会自动加载 hook —— 这是一个 skill suite,不是独立 agent 插件
266
300
 
267
301
  ## CLI 入口
@@ -279,7 +313,8 @@ CLI 暴露的子命令:
279
313
  | 安装助手 | `dev-memory-cli install-hooks <codex\|claude\|--all>` | 把 hook 模板合并到目标配置 |
280
314
  | 浏览器 UI | `dev-memory-cli ui [--port N] [--read-only]` | 启动本地浏览器界面看/编辑记忆 |
281
315
  | 分支生命周期 | `dev-memory-cli branch [list\|inspect\|rename\|fork\|delete\|init]` | 分支记忆迁移 / 副本 / 重置(无参数 = 交互式 type-ahead)|
282
- | Skill 工作流(agent 在 SKILL.md 里调用,也能手动跑)| `dev-memory-cli capture <record\|rewrite-entry\|show\|suggest-kind\|...>` | 统一写入入口(含 0.18 起的 dedup 拦截 + rewrite-entry) |
316
+ | Skill 工作流(agent 在 SKILL.md 里调用,也能手动跑)| `dev-memory-cli read <show\|search>` | 主动读取入口:精确定位当前 repo+branch 记忆路径,按关键词只搜当前 repo 的记忆 |
317
+ | | `dev-memory-cli capture <record\|rewrite-entry\|delete-entry\|apply-summary-output\|show\|suggest-kind\|...>` | 统一写入入口(含 dedup、结构化 patch executor) |
283
318
  | | `dev-memory-cli setup <init\|merge-unsorted\|mark-completed>` | 整理 unsorted |
284
319
  | | `dev-memory-cli tidy <prepare\|apply>` | 浏览器化的批量 review + 落盘(0.18 起含 annotated md + delete-block)|
285
320
  | | `dev-memory-cli graduate <dry-run\|apply\|index>` | 分支归档 + 跨分支知识上提 |
@@ -298,6 +333,7 @@ hooks/
298
333
  README.md
299
334
  lib/ # 所有 Python 业务逻辑都集中在这里(npm 包发布时随包带)
300
335
  dev_memory_common.py # 共享公共库(path 解析、template、git facts、merge helpers ...)
336
+ dev_memory_read.py # `dev-memory-cli read` 子命令实现(只读定位 + 搜索)
301
337
  dev_memory_context.py # `dev-memory-cli context` 子命令实现
302
338
  dev_memory_capture.py # `dev-memory-cli capture` 子命令实现
303
339
  dev_memory_setup.py # `dev-memory-cli setup` 子命令实现
@@ -312,6 +348,7 @@ scripts/
312
348
  install_suite.py # 本地开发用的 symlink 安装器
313
349
  npm/ # 打包 check/build 助手
314
350
  skills/ # SKILL.md + agents/openai.yaml + references/ 三件套;不再带 scripts/
351
+ dev-memory-read/
315
352
  dev-memory-capture/
316
353
  dev-memory-setup/
317
354
  dev-memory-tidy/
package/bin/dev-memory.js CHANGED
@@ -7,6 +7,7 @@ const os = require("node:os");
7
7
 
8
8
  const PACKAGE_ROOT = path.resolve(__dirname, "..");
9
9
  const DEFAULT_STORAGE_ROOT = path.join(os.homedir(), ".dev-memory", "repos");
10
+ const DEFAULT_CONFIG_PATH = process.env.DEV_MEMORY_CONFIG_PATH || path.join(os.homedir(), ".dev-memory", "config.json");
10
11
 
11
12
  function fail(message) {
12
13
  process.stderr.write(`ERROR: ${message}\n`);
@@ -116,6 +117,72 @@ function writeJson(filePath, data) {
116
117
  fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
117
118
  }
118
119
 
120
+ function commandExists(command) {
121
+ const result = spawnSync("sh", ["-lc", `command -v ${command}`], {
122
+ encoding: "utf8",
123
+ stdio: ["ignore", "pipe", "ignore"],
124
+ });
125
+ return result.status === 0 && Boolean((result.stdout || "").trim());
126
+ }
127
+
128
+ function detectSessionSummaryCommand() {
129
+ if (commandExists("coco")) {
130
+ return {
131
+ provider: "coco",
132
+ command: "coco -p --yolo --session-id {summary_session_id} {prompt}",
133
+ };
134
+ }
135
+ if (commandExists("codex")) {
136
+ return {
137
+ provider: "codex",
138
+ command: "codex exec --ignore-user-config --ignore-rules --skip-git-repo-check --sandbox danger-full-access {prompt}",
139
+ };
140
+ }
141
+ if (commandExists("claude")) {
142
+ return {
143
+ provider: "claude",
144
+ command: "claude -p --permission-mode bypassPermissions --session-id {summary_session_uuid} {prompt}",
145
+ };
146
+ }
147
+ return null;
148
+ }
149
+
150
+ function ensureSessionSummaryConfig() {
151
+ const configPath = DEFAULT_CONFIG_PATH;
152
+ const existing = fs.existsSync(configPath) ? loadJson(configPath) : {};
153
+ const config = existing && typeof existing === "object" ? existing : {};
154
+ const sessionSummary = config.session_summary && typeof config.session_summary === "object"
155
+ ? { ...config.session_summary }
156
+ : {};
157
+ if (typeof sessionSummary.command === "string" && sessionSummary.command.trim()) {
158
+ return {
159
+ path: configPath,
160
+ changed: false,
161
+ provider: sessionSummary.provider || null,
162
+ command: sessionSummary.command,
163
+ };
164
+ }
165
+ const detected = detectSessionSummaryCommand();
166
+ if (!detected) {
167
+ return { path: configPath, changed: false, provider: null, command: null };
168
+ }
169
+ config.session_summary = {
170
+ ...sessionSummary,
171
+ provider: detected.provider,
172
+ command: detected.command,
173
+ max_attempts: sessionSummary.max_attempts || 3,
174
+ configured_at: new Date().toISOString(),
175
+ source: "install-hooks:auto-detect",
176
+ };
177
+ writeJson(configPath, config);
178
+ return {
179
+ path: configPath,
180
+ changed: true,
181
+ provider: detected.provider,
182
+ command: detected.command,
183
+ };
184
+ }
185
+
119
186
  function templatePathForAgent(agent) {
120
187
  if (agent === "codex") return packageScript("hooks", "codex-hooks.json");
121
188
  if (agent === "claude") return packageScript("hooks", "hooks.json");
@@ -186,7 +253,14 @@ function installHooksForAgent(agent, options) {
186
253
  const existing = fs.existsSync(targetPath) ? loadJson(targetPath) : {};
187
254
  const merged = mergeConfig(existing, template);
188
255
  writeJson(targetPath, merged);
189
- const report = { agent, scope, target: targetPath, events: Object.keys(template.hooks || {}) };
256
+ const summaryConfig = ensureSessionSummaryConfig();
257
+ const report = {
258
+ agent,
259
+ scope,
260
+ target: targetPath,
261
+ events: Object.keys(template.hooks || {}),
262
+ session_summary_config: summaryConfig,
263
+ };
190
264
  if (repoRoot) report.repo_root = repoRoot;
191
265
  return report;
192
266
  }
@@ -211,11 +285,13 @@ function commandUi(_positional, options) {
211
285
  // Their CLIs (subcommands, flags) are owned by argparse on the Python
212
286
  // side, so we deliberately bypass parseArgs and forward raw argv.
213
287
  const PY_SUBCOMMAND_SCRIPTS = {
288
+ read: "dev_memory_read.py",
214
289
  context: "dev_memory_context.py",
215
290
  capture: "dev_memory_capture.py",
216
291
  setup: "dev_memory_setup.py",
217
292
  graduate: "dev_memory_graduate.py",
218
293
  tidy: "dev_memory_tidy.py",
294
+ summary: "dev_memory_summary.py",
219
295
  };
220
296
 
221
297
  function commandPySubcommand(name, rawArgs) {
@@ -541,11 +617,13 @@ function printHelp() {
541
617
  dev-memory-cli install-hooks <codex|claude> [--repo PATH] [--global]
542
618
  dev-memory-cli install-hooks --all [--repo PATH] [--global]
543
619
  dev-memory-cli ui [--port N] [--host HOST] [--no-open] [--read-only]
620
+ dev-memory-cli read <show|search> [...]
544
621
  dev-memory-cli context <show|...> [...]
545
622
  dev-memory-cli capture <record|show|sync-working-tree|record-head|suggest-kind|classify> [...]
546
623
  dev-memory-cli setup <init|merge-unsorted|mark-completed> [...]
547
624
  dev-memory-cli graduate <dry-run|apply|index> [...]
548
625
  dev-memory-cli tidy <prepare|apply> [...]
626
+ dev-memory-cli summary <extract-core> [...]
549
627
  dev-memory-cli branch [list|inspect|rename|fork|delete|init|inherit-worktree-base] [...] # no subcommand = interactive
550
628
 
551
629
  Environment:
package/hooks/README.md CHANGED
@@ -54,13 +54,19 @@ dev-memory-cli install-hooks claude
54
54
  语义分别是:
55
55
 
56
56
  - `SessionStart`
57
- 读取当前 repo+branch 的 dev-memory,并把可恢复上下文注入新会话
57
+ 读取当前 repo+branch 的 dev-memory,并把可恢复上下文注入新会话;同一 session resume 重触发时通过
58
+ `<repo-memory>/jobs/session-start/injected/*.json` 幂等跳过重复注入,只写 skip 日志并返回空 payload
58
59
  - `PreCompact`
59
60
  在上下文压缩前刷新 working-tree 派生导航信息
60
61
  - `Stop`
61
62
  在每次回复后记录轻量 HEAD 标记
62
63
  - `SessionEnd`
63
- 在会话结束时再落一次最终 HEAD 标记
64
+ 在会话结束时再落一次最终 HEAD 标记,并把 transcript 总结任务 enqueue 到
65
+ `<repo-memory>/jobs/session-summary/pending/`。`install-hooks` 会扫描本地工具并写入
66
+ `~/.dev-memory/config.json` 的 `session_summary.command`(优先 coco,其次 codex,再 claude)。hook 会启动后台 worker,先生成
67
+ `<repo-memory>/jobs/session-summary/inputs/*.json`,再把已提取的 core messages 和 existing memory
68
+ 内联进 `{prompt}`。agent 只输出 summary-output JSON,不调用 CLI;worker 负责 JSON 校验、同 session 最多 3 次重试、落盘和 pending/done/skipped/failed 迁移;无 touched targets 的空总结进入 `skipped/`,不算真实落盘。hook 不等待总结完成。`DEV_MEMORY_SESSION_SUMMARY_CMD` 仅作临时 override,禁用用
69
+ `DEV_MEMORY_DISABLE_SESSION_SUMMARY_AGENT=1`
64
70
 
65
71
  ## 和 ECC 的差异
66
72
 
package/hooks/hooks.json CHANGED
@@ -52,7 +52,7 @@
52
52
  "timeout": 15
53
53
  }
54
54
  ],
55
- "description": "Persist final HEAD marker at session end",
55
+ "description": "Persist final HEAD marker and enqueue transcript summary work",
56
56
  "id": "dev-memory:session-end"
57
57
  }
58
58
  ]