dev-memory-cli 0.22.1 → 0.23.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/bin/dev-memory.js CHANGED
@@ -56,7 +56,11 @@ function runPython(scriptPath, args, cwd = process.cwd(), extraEnv = {}) {
56
56
  // inject DEFAULT_STORAGE_ROOT here — that would short-circuit Python's
57
57
  // fallback chain (which prefers legacy ~/.dev-assets if it has data and
58
58
  // ~/.dev-memory does not).
59
- const env = { ...process.env, ...extraEnv };
59
+ const env = {
60
+ ...process.env,
61
+ DEV_MEMORY_CLI_PATH: process.env.DEV_MEMORY_CLI_PATH || path.resolve(process.argv[1]),
62
+ ...extraEnv,
63
+ };
60
64
  const result = spawnSync(python, [scriptPath, ...args], {
61
65
  cwd,
62
66
  env,
@@ -136,7 +140,7 @@ function detectSessionSummaryCommand() {
136
140
  if (commandExists("codex")) {
137
141
  return {
138
142
  provider: "codex",
139
- command: "codex exec --ignore-user-config --ignore-rules --skip-git-repo-check --sandbox danger-full-access {prompt}",
143
+ command: "codex exec --ephemeral --ignore-user-config --ignore-rules --skip-git-repo-check --sandbox danger-full-access {prompt}",
140
144
  };
141
145
  }
142
146
  if (commandExists("claude")) {
@@ -361,6 +365,7 @@ const PY_SUBCOMMAND_SCRIPTS = {
361
365
  graduate: "dev_memory_graduate.py",
362
366
  tidy: "dev_memory_tidy.py",
363
367
  summary: "dev_memory_summary.py",
368
+ "session-scan": "dev_memory_session_scan.py",
364
369
  };
365
370
 
366
371
  function commandPySubcommand(name, rawArgs) {
@@ -371,6 +376,112 @@ function commandPySubcommand(name, rawArgs) {
371
376
  runPython(scriptPath, rawArgs);
372
377
  }
373
378
 
379
+ const MAINTENANCE_MODES = new Set(["tidy", "archive"]);
380
+ const MAINTENANCE_MARKER = "DEV_MEMORY_INTERNAL_MAINTENANCE_AGENT_V1";
381
+
382
+ function maintenanceHelp() {
383
+ process.stdout.write(`Usage:
384
+ dev-memory-cli maintain tidy [--repo PATH] [--branch NAME] [--scope branch|branch+repo]
385
+ [--executor auto|codex|coco] [--model MODEL]
386
+ dev-memory-cli maintain archive [--repo PATH] [--branch NAME]
387
+ [--executor auto|codex|coco] [--model MODEL]
388
+ dev-memory-cli maintain <tidy|archive> --print-prompt [other options]
389
+
390
+ The command starts a dedicated interactive maintenance-agent session. Tidy
391
+ must not apply destructive changes before HTML review; archive must not apply
392
+ before dry-run review and explicit confirmation.
393
+ `);
394
+ }
395
+
396
+ function resolveMaintenanceExecutor(requested) {
397
+ const name = requested || process.env.DEV_MEMORY_MAINTENANCE_EXECUTOR || "auto";
398
+ if (!new Set(["auto", "codex", "coco"]).has(name)) {
399
+ fail(`unsupported maintenance executor: ${name}`);
400
+ }
401
+ if (name !== "auto") return name;
402
+ for (const candidate of ["codex", "coco"]) {
403
+ if (commandExists(candidate)) return candidate;
404
+ }
405
+ fail("no maintenance executor found; install codex/coco or use --print-prompt");
406
+ }
407
+
408
+ function buildMaintenancePrompt(mode, options, repoRoot) {
409
+ const promptPath = packageScript("lib", "maintenance", `${mode}.md`);
410
+ if (!fs.existsSync(promptPath)) fail(`missing maintenance prompt: ${promptPath}`);
411
+ const workflow = fs.readFileSync(promptPath, "utf8").trim();
412
+ const cliPath = path.resolve(process.argv[1]);
413
+ const branch = options.branch || "<current-git-branch>";
414
+ const scope = mode === "tidy" ? (options.scope || "branch") : "branch";
415
+ return `${MAINTENANCE_MARKER}
416
+
417
+ 你是 dev-memory 的专用维护 Agent。本会话只处理下面指定仓库的记忆维护,不承担普通开发任务。
418
+
419
+ 目标仓库:${repoRoot}
420
+ 目标分支:${branch}
421
+ 维护模式:${mode}
422
+ 整理范围:${scope}
423
+ 本次必须使用的 CLI:node ${JSON.stringify(cliPath)}
424
+
425
+ 不要依赖全局 dev-memory capture/setup/tidy/graduate Skill;完整维护流程已经随本提示提供。
426
+ 涉及删除、改写或归档时,必须遵守下面流程中的人工审核和确认门禁。
427
+
428
+ ${workflow}
429
+ `;
430
+ }
431
+
432
+ function commandMaintain(rawArgs) {
433
+ const mode = rawArgs[0];
434
+ if (!mode || mode === "--help" || mode === "-h") {
435
+ maintenanceHelp();
436
+ return;
437
+ }
438
+ if (!MAINTENANCE_MODES.has(mode)) {
439
+ fail(`unknown maintenance mode: ${mode}`);
440
+ }
441
+ const { positional, options } = parseArgs(rawArgs.slice(1));
442
+ if (positional.length) fail(`unexpected maintenance argument: ${positional[0]}`);
443
+ const repoRoot = path.resolve(options.repo || process.cwd());
444
+ if (!fs.existsSync(repoRoot) || !fs.statSync(repoRoot).isDirectory()) {
445
+ fail(`maintenance repo does not exist: ${repoRoot}`);
446
+ }
447
+ if (mode === "tidy" && options.scope && !new Set(["branch", "branch+repo"]).has(options.scope)) {
448
+ fail(`unsupported tidy scope: ${options.scope}`);
449
+ }
450
+ const prompt = buildMaintenancePrompt(mode, options, repoRoot);
451
+ if (options["print-prompt"] || options["dry-run"]) {
452
+ process.stdout.write(prompt);
453
+ return;
454
+ }
455
+
456
+ const executor = resolveMaintenanceExecutor(options.executor);
457
+ let args;
458
+ if (executor === "codex") {
459
+ const modelArgs = options.model ? ["--model", String(options.model)] : [];
460
+ args = [
461
+ "-C", repoRoot,
462
+ "--sandbox", "danger-full-access",
463
+ "--ask-for-approval", "on-request",
464
+ "--dangerously-bypass-hook-trust",
465
+ ...modelArgs,
466
+ prompt,
467
+ ];
468
+ } else {
469
+ const modelArgs = options.model ? ["--config", `model=${String(options.model)}`] : [];
470
+ args = ["--permission-mode", "default", ...modelArgs, prompt];
471
+ }
472
+ const result = spawnSync(executor, args, {
473
+ cwd: repoRoot,
474
+ env: {
475
+ ...process.env,
476
+ DEV_MEMORY_MAINTENANCE_AGENT: "1",
477
+ DEV_MEMORY_MAINTENANCE_MODE: mode,
478
+ },
479
+ stdio: "inherit",
480
+ });
481
+ if (result.error) fail(`unable to start ${executor}: ${result.error.message}`);
482
+ if (result.status !== 0) process.exit(result.status || 1);
483
+ }
484
+
374
485
  function branchScript() {
375
486
  return packageScript("lib", "dev_memory_branch.py");
376
487
  }
@@ -687,13 +798,17 @@ function printHelp() {
687
798
  dev-memory-cli install-hooks --all [--repo PATH] [--global]
688
799
  dev-memory-cli ui [--port N] [--host HOST] [--no-open] [--read-only]
689
800
  dev-memory-cli workspace <show|primary> [...]
801
+ dev-memory-cli init [--repo PATH] [--branch NAME]
690
802
  dev-memory-cli read <show|search> [...]
803
+ dev-memory-cli maintain <tidy|archive> [...] # starts a dedicated interactive agent
691
804
  dev-memory-cli context <show|...> [...]
805
+ # Low-level mutation/admin commands used by session-scan and maintenance agents:
692
806
  dev-memory-cli capture <record|show|sync-working-tree|record-head|suggest-kind|classify> [...]
693
807
  dev-memory-cli setup <init|merge-unsorted|mark-completed> [...]
694
808
  dev-memory-cli graduate <dry-run|apply|index> [...]
695
809
  dev-memory-cli tidy <prepare|apply> [...]
696
810
  dev-memory-cli summary <extract-core> [...]
811
+ dev-memory-cli session-scan <run|install|status|stats|history|show|uninstall|config> [...]
697
812
  dev-memory-cli branch [list|inspect|rename|fork|delete|init|inherit-worktree-base] [...] # no subcommand = interactive
698
813
 
699
814
  Environment:
@@ -713,6 +828,14 @@ function main() {
713
828
  commandPySubcommand(command, argv.slice(1));
714
829
  return;
715
830
  }
831
+ if (command === "init") {
832
+ commandPySubcommand("setup", ["init", ...argv.slice(1)]);
833
+ return;
834
+ }
835
+ if (command === "maintain") {
836
+ commandMaintain(argv.slice(1));
837
+ return;
838
+ }
716
839
  if (command === "branch") {
717
840
  commandBranch(argv.slice(1));
718
841
  return;
package/hooks/README.md CHANGED
@@ -1,19 +1,17 @@
1
1
  # Lifecycle Hooks
2
2
 
3
- 这套仓库不再使用 Git hooks
4
-
5
- 现在采用的是接近 ECC 的生命周期 hooks,并同时支持 Claude 和 Codex 两种 agent。
3
+ 本项目使用 agent 生命周期 hook,不使用 Git hookClaude Code 与 Codex 的可用事件不同,模板分别维护。
6
4
 
7
5
  ## 当前仓库里的接入方式
8
6
 
9
- - 推荐的 repo-local 落地点:
7
+ - repo-local 配置位置:
10
8
  - Claude: `.claude/settings.local.json`
11
9
  - Codex: `.codex/hooks.json`
12
10
  - 可复用模板:
13
11
  - Claude: [hooks/hooks.json](hooks.json)
14
12
  - Codex: [hooks/codex-hooks.json](codex-hooks.json)
15
13
  - `dev-memory-cli` 是这两套配置共用的稳定执行入口
16
- - 是否自动生效取决于你本地是否把对应配置文件落到了各自约定位置,而不是模板文件本身
14
+ - hook 仅在模板内容合并到对应 agent 配置后生效
17
15
 
18
16
  ### Codex 快速安装
19
17
 
@@ -40,48 +38,184 @@ dev-memory-cli install-hooks claude
40
38
 
41
39
  已有旧配置如果还写着 `dev-memory hook ...` 或 `npx dev-memory hook ...`,重新执行安装命令会按 hook id 覆盖为 `dev-memory-cli hook ...`。
42
40
 
43
- ## 这些 hook 做什么
44
-
45
- - Claude:
46
- - `SessionStart`
47
- - `PreCompact`
48
- - `Stop`
49
- - `SessionEnd`
50
- - Codex:
51
- - `SessionStart`
52
- - `Stop`
53
-
54
- 语义分别是:
55
-
56
- - `SessionStart`
57
- 读取当前 repo+branch 的 dev-memory,并把可恢复上下文注入新会话;同一 session resume 重触发时通过
58
- `<repo-memory>/jobs/session-start/injected/*.json` 幂等跳过重复注入,只写 skip 日志并返回空 payload
59
- - `PreCompact`
60
- 在上下文压缩前刷新 working-tree 派生导航信息
61
- - `Stop`
62
- 在每次回复后记录轻量 HEAD 标记
63
- - `SessionEnd`
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`
70
-
71
- ## ECC 的差异
72
-
73
- ECC 是插件形态,安装后可以靠插件机制自动加载 hook 配置。
74
-
75
- 这个仓库当前是 skill suite,不是独立 Claude 插件,所以:
76
-
77
- - 对本仓库自身开发:
78
- - Claude 把 [hooks/hooks.json](hooks.json) 合并到本地 `.claude/settings.local.json`
79
- - Codex 把 [hooks/codex-hooks.json](codex-hooks.json) 放到或合并到本地 `.codex/hooks.json`
80
- - 对其他仓库:
81
- - 先安装 `dev-memory-cli`
82
- - 再把模板 merge 到对应 repo-local 配置
83
- - hook 运行时统一走 `dev-memory-cli hook ...`
84
- - 不能假装成“像插件一样对所有仓库自动加载”
41
+ ## Hook 行为
42
+
43
+ | 事件 | Codex CLI | Claude Code CLI | 行为 |
44
+ | --- | :-: | :-: | --- |
45
+ | `SessionStart` | ✓ | ✓ | 读取当前 repo+branch 记忆并注入会话;同一 session 的重复触发幂等跳过 |
46
+ | `Stop` | ✓ | ✓ | 每次回复后记录轻量 HEAD marker;Codex 同时登记定时扫描候选 |
47
+ | `PreCompact` | | ✓ | 兼容占位;当前不执行额外刷新 |
48
+ | `SessionEnd` | | ✓ | 记录最终 HEAD,创建 transcript 总结任务并启动后台 worker |
49
+
50
+ `SessionStart` 的幂等记录位于 `<repo-memory>/jobs/session-start/injected/*.json`。重复触发只记录 skip 日志,不重复注入上下文。
51
+
52
+ ## 会话总结
53
+
54
+ ### 生效范围
55
+
56
+ `SessionEnd` 自动总结仅由 **Claude Code CLI** 的生命周期事件触发。Codex CLI hook 模板没有 `SessionEnd`;它的 `Stop` 只登记候选,不在每轮回答后启动模型。Codex Desktop 不使用这组项目 hook。
57
+
58
+ 总结任务的触发端与执行端是两个独立概念:
59
+
60
+ - **触发端**:Claude Code CLI 的 `SessionEnd` hook。
61
+ - **执行端**:本机可用的 `coco`、`codex` 或 `claude` CLI。
62
+
63
+ 因此,执行总结所用的 CLI 与产生原会话的客户端没有绑定关系。
64
+
65
+ ### 总结工具选择
66
+
67
+ `install-hooks` 检测本地命令并初始化 `~/.dev-memory/config.json`。已有非空 `session_summary.command` 时保留现有配置,否则按以下顺序选择第一个可用工具:
68
+
69
+ 单独执行 `install-hooks codex` 也会初始化这项 CLI 配置,但不会为 Codex CLI 增加 `SessionEnd` 事件;没有 Claude Code `SessionEnd` 或其它显式 enqueue 来源时,该配置不会自动启动总结任务。
70
+
71
+ | 优先级 | 工具 | 默认命令 |
72
+ | --- | --- | --- |
73
+ | 1 | `coco` | `coco -p --yolo --session-id {summary_session_id} {prompt}` |
74
+ | 2 | `codex` | `codex exec --ephemeral --ignore-user-config --ignore-rules --skip-git-repo-check --sandbox danger-full-access {prompt}` |
75
+ | 3 | `claude` | `claude -p --permission-mode bypassPermissions --session-id {summary_session_uuid} {prompt}` |
76
+
77
+ 示例配置:
78
+
79
+ ```json
80
+ {
81
+ "session_summary": {
82
+ "provider": "codex",
83
+ "command": "codex exec --ephemeral --ignore-user-config --ignore-rules --skip-git-repo-check --sandbox danger-full-access {prompt}",
84
+ "max_attempts": 3
85
+ }
86
+ }
87
+ ```
88
+
89
+ 该机制依赖本地 CLI 命令,模型、账号和认证配置沿用被选 CLI;dev-memory 不直接集成 provider HTTP API。`session_summary.command` 支持自定义其它命令,`DEV_MEMORY_SESSION_SUMMARY_CMD` 提供进程级临时 override;`DEV_MEMORY_DISABLE_SESSION_SUMMARY_AGENT=1` 禁用后台总结执行。
90
+
91
+ ### 总结作用
92
+
93
+ `SessionEnd` 总结用于从一次会话中提取对后续开发仍然有效的语义信息,而不是保存 transcript 副本或生成 changelog。处理流程如下:
94
+
95
+ 1. `SessionEnd` 记录最终 HEAD,并把 job 写入 `<repo-memory>/jobs/session-summary/pending/`。
96
+ 2. worker 从 transcript 中提取核心 user/assistant 文本,并加载现有 branch/repo 记忆。
97
+ 3. 总结 CLI 只生成 summary-output JSON,不直接调用 dev-memory 命令。
98
+ 4. worker 校验 JSON,最多重试 `max_attempts` 次,再通过代码应用结构化 patch。
99
+ 5. job 根据结果移动到 `done/`、`skipped/` 或 `failed/`。
100
+
101
+ 允许写入或修正的内容包括:
102
+
103
+ - decisions、risks、glossary
104
+ - 功能文件索引 `file_map`
105
+ - repo 级 shared decisions、context、sources
106
+ - 已有 entry 的 rewrite 或 delete
107
+
108
+ 以下内容不会写入:
109
+
110
+ - 工具调用流水账、system 消息、reasoning
111
+ - 完整 transcript 或提交历史副本
112
+ - “当前进展”“下一步”“当前阶段”等时效性状态
113
+ - 与现有记忆相比没有有效变化的内容
114
+
115
+ 无有效变化的 job 进入 `skipped/`,不会刷新 capture manifest,也不计为真实记忆写入。hook 采用后台执行,不等待总结完成。
116
+
117
+ ## Codex 定时扫描
118
+
119
+ Codex CLI 和 Desktop 共用本地 rollout 文件:
120
+
121
+ ```text
122
+ ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl
123
+ ~/.codex/archived_sessions/
124
+ ```
125
+
126
+ 安装 macOS LaunchAgent:
127
+
128
+ ```bash
129
+ dev-memory-cli session-scan install
130
+ dev-memory-cli session-scan status
131
+ ```
132
+
133
+ 任务默认每天本地时间 03:00 和 13:00 运行。LaunchAgent 使用 `session-scan run --scheduled`,运行前检查 macOS HID 空闲时长;最近 10 分钟有键鼠输入时写入 `skipped_active` 记录并退出,不读取会话正文、不调用模型。检测不可用时默认保守跳过。手工执行 `session-scan run` 不启用活跃检测。
134
+
135
+ 首次扫描只回看最近 3 天;后续使用持久化字节游标补扫上次成功处理后产生的全部数据。`install-hooks codex` 与 `session-scan install` 相互独立。
136
+
137
+ 扫描器只提取尚未处理的 user/assistant 语义消息,不限制消息数量,也不截断单条消息。材料超过单次模型上下文时按顺序分块,每个分块都进入中间摘要,再结合现有 memory 生成最终结构化结果。游标只在最终结果成功应用后推进。
138
+
139
+ ### 执行器配置
140
+
141
+ `~/.dev-memory/config.json` 中的 `session_scan` 独立配置扫描执行器。默认提供 `coco`、`codex`、`claude` 三个 preset,并按顺序选择第一个已安装且启用的命令:
142
+
143
+ ```json
144
+ {
145
+ "session_scan": {
146
+ "executor": "auto",
147
+ "order": ["coco", "codex", "claude"],
148
+ "schedule_times": ["03:00", "13:00"],
149
+ "skip_when_computer_active": true,
150
+ "active_within_minutes": 10,
151
+ "activity_check_fail_closed": true,
152
+ "chunk_chars": 60000,
153
+ "idle_minutes": 60,
154
+ "first_lookback_days": 3,
155
+ "executors": {
156
+ "coco": {"enabled": true, "command": "coco", "model": null, "profile": null, "extra_args": [], "env": {}},
157
+ "codex": {"enabled": true, "command": "codex", "model": null, "profile": null, "extra_args": [], "env": {}},
158
+ "claude": {"enabled": true, "command": "claude", "model": null, "profile": null, "extra_args": [], "env": {}}
159
+ }
160
+ }
161
+ }
162
+ ```
163
+
164
+ `model` 由内置适配器转换为对应 CLI 参数。`profile`、`extra_args` 和 `env` 用于选择账号、模型供应商、本地 provider 或代理。也可以增加自定义 preset;自定义命令需要自行保证结构化 JSON 输出。
165
+
166
+ ```bash
167
+ dev-memory-cli session-scan config show
168
+ dev-memory-cli session-scan config set-executor codex
169
+ dev-memory-cli session-scan config set-model codex <model>
170
+ dev-memory-cli session-scan config set-profile codex <profile>
171
+ dev-memory-cli session-scan config set-schedule 03:00 13:00
172
+ dev-memory-cli session-scan config set-active-minutes 10
173
+ dev-memory-cli session-scan config set-active-check on
174
+ dev-memory-cli session-scan config validate
175
+ ```
176
+
177
+ 已安装定时任务时,`set-schedule` 会自动重载 LaunchAgent;活跃阈值和开关由每次运行动态读取,无需重装。
178
+
179
+ `auto` 只在命令不存在或 preset 被禁用时选择下一个执行器。模型调用失败不会自动换供应商,以免重复产生不可控费用。
180
+
181
+ ### 递归防护
182
+
183
+ Codex preset 固定使用 `codex exec --ephemeral --json`,总结调用不会写入新的 rollout。扫描器还会登记内部 session/thread ID,并排除 `dev-memory-summary-` session 和带内部 prompt marker 的会话。即使自定义执行器产生持久化会话,也不会被下一轮当成业务会话重复总结。
184
+
185
+ ### 账本与用量
186
+
187
+ ```text
188
+ ~/.dev-memory/jobs/session-scan/
189
+ candidates/ # Codex Stop 登记
190
+ state/ # 会话字节游标
191
+ sessions/ # 原始大小、分块和用量摘要
192
+ runs/ # 每次扫描记录
193
+ logs/ # LaunchAgent stdout/stderr
194
+ events.jsonl
195
+ internal-sessions.jsonl
196
+ ```
197
+
198
+ 每个 run 记录原文件大小、本轮新增字节、语义消息和字符数、原会话累计 token、每次总结调用的执行器/模型/耗时/token,以及成功、跳过和失败状态。执行器没有返回 usage 时写为 `unavailable`,不会伪装成 0。
199
+
200
+ ```bash
201
+ dev-memory-cli session-scan run --dry-run --json
202
+ dev-memory-cli session-scan run
203
+ dev-memory-cli session-scan stats --json
204
+ dev-memory-cli session-scan history --limit 20
205
+ dev-memory-cli session-scan show <run-id>
206
+ dev-memory-cli session-scan uninstall
207
+ ```
208
+
209
+ `dev-memory-cli ui` 的“会话扫描”视图读取同一账本,展示仓库扫描次数、会话数量、原始大小、新增数据量和每次总结 token。
210
+
211
+ ## 接入边界
212
+
213
+ 这个仓库是 skill suite,不是自动注入所有项目的独立插件:
214
+
215
+ - Claude 把 [hooks/hooks.json](hooks.json) 合并到 `.claude/settings.local.json`。
216
+ - Codex CLI 把 [hooks/codex-hooks.json](codex-hooks.json) 合并到 `.codex/hooks.json`。
217
+ - Codex Desktop 通过本地 rollout 定时扫描覆盖,不依赖项目 hook。
218
+ - 其他仓库需要先安装 CLI,再安装对应 hook;hook 运行时统一调用 `dev-memory-cli hook ...`。
85
219
 
86
220
  ## 原则
87
221
 
@@ -24,7 +24,7 @@
24
24
  "timeout": 15
25
25
  }
26
26
  ],
27
- "description": "Persist lightweight HEAD markers after each response",
27
+ "description": "Persist HEAD markers and register the session for scheduled scanning",
28
28
  "id": "dev-memory:stop"
29
29
  }
30
30
  ]
package/hooks/hooks.json CHANGED
@@ -24,7 +24,7 @@
24
24
  "timeout": 20
25
25
  }
26
26
  ],
27
- "description": "Refresh working-tree-derived navigation before compaction",
27
+ "description": "Compatibility no-op; SessionStart handles working-tree refresh",
28
28
  "id": "dev-memory:pre-compact"
29
29
  }
30
30
  ],