dev-memory-cli 0.23.1 → 0.25.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/README.md +43 -44
- package/bin/dev-memory.js +147 -7
- package/hooks/README.md +24 -11
- package/hooks/trae-hooks.json +29 -0
- package/lib/dev_memory_session_scan.py +4 -2
- package/lib/maintenance/archive.md +83 -0
- package/lib/maintenance/tidy.md +102 -0
- package/package.json +2 -1
- package/scripts/hooks/_common.py +4 -5
- package/suite-manifest.json +4 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Dev Memory Skill Suite
|
|
2
2
|
|
|
3
|
-
Dev Memory Skill Suite 为 Codex、Claude 等 coding agent 提供跨会话开发记忆能力。
|
|
3
|
+
Dev Memory Skill Suite 为 Codex、Claude Code、Trae / Trae CN 等 coding agent 提供跨会话开发记忆能力。
|
|
4
4
|
|
|
5
5
|
开发知识存储在用户目录,并按 **仓库身份 + Git 分支** 隔离。分支层保存当前工作的目标、约束和上下文,repo 层保存跨分支长期有效的规则与资料入口。Git 负责提交与代码历史,dev-memory 负责对后续开发仍有价值的语义信息。
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ Dev Memory Skill Suite 为 Codex、Claude 等 coding agent 提供跨会话开发
|
|
|
9
9
|
## 核心能力
|
|
10
10
|
|
|
11
11
|
- **会话恢复**:`SessionStart` 自动注入当前 repo + branch 的浓缩记忆和权威文件路径。
|
|
12
|
-
-
|
|
12
|
+
- **后台语义沉淀**:定期读取完整会话,对照已有记忆后执行 append、rewrite、delete 或明确 skip。
|
|
13
13
|
- **纠错优先**:修正旧记忆时定位并改写原 entry,避免同时保留互相冲突的新旧结论。
|
|
14
14
|
- **分支隔离**:同一仓库的不同分支拥有独立工作记忆,可 fork、rename、reset 或归档。
|
|
15
15
|
- **跨分支共享**:稳定规则进入 repo 层;候选知识可在分支完成时提炼上提。
|
|
@@ -17,17 +17,13 @@ Dev Memory Skill Suite 为 Codex、Claude 等 coding agent 提供跨会话开发
|
|
|
17
17
|
- **多仓库工作区**:一个 workspace 下可同时加载多个 repo,并为主仓库保留更完整的上下文。
|
|
18
18
|
- **本地管理面板**:浏览、编辑已存储记忆,预览 SessionStart 注入文本,并查看会话扫描与 token 用量。
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## 一个常驻 Skill
|
|
21
21
|
|
|
22
22
|
| Skill | 职责 | 适用场景 |
|
|
23
23
|
| --- | --- | --- |
|
|
24
24
|
| `dev-memory-read` | 定位并搜索当前 repo/branch 的权威记忆,只读不写 | 主动恢复既有记忆、查询历史 TODO |
|
|
25
|
-
| `dev-memory-capture` | 写入、改写或删除记忆;同步 Git 派生索引 | 稳定结论沉淀、纠错、阶段 checkpoint |
|
|
26
|
-
| `dev-memory-setup` | 把 `unsorted.md` 中的内容分类合并到结构化文件 | 未分类内容整理、记忆结构初始化 |
|
|
27
|
-
| `dev-memory-tidy` | 批量审查陈旧、重复、错误或模板残留条目,备份后应用 | 结构化记忆校准与清理 |
|
|
28
|
-
| `dev-memory-graduate` | 提炼跨分支知识并归档已完成分支 | 分支收尾与归档 |
|
|
29
25
|
|
|
30
|
-
Skill
|
|
26
|
+
普通开发会话不再常驻 capture/setup/tidy/graduate Skill。会话写入由 session-scan 后台处理;整理和归档由 CLI 临时启动专用维护 Agent,并只把对应维护 prompt 放进那个独立会话。
|
|
31
27
|
|
|
32
28
|
## 安装
|
|
33
29
|
|
|
@@ -44,13 +40,13 @@ npx skills add xluos/dev-memory-skill-suite --list
|
|
|
44
40
|
安装到 Codex 全局:
|
|
45
41
|
|
|
46
42
|
```bash
|
|
47
|
-
npx skills add xluos/dev-memory-skill-suite --skill
|
|
43
|
+
npx skills add xluos/dev-memory-skill-suite --skill dev-memory-read -a codex -g -y
|
|
48
44
|
```
|
|
49
45
|
|
|
50
46
|
安装到检测到的所有 agent:
|
|
51
47
|
|
|
52
48
|
```bash
|
|
53
|
-
npx skills add xluos/dev-memory-skill-suite --all -g -y
|
|
49
|
+
npx skills add xluos/dev-memory-skill-suite --skill dev-memory-read --all -g -y
|
|
54
50
|
```
|
|
55
51
|
|
|
56
52
|
### 安装 CLI 与 Hook
|
|
@@ -61,8 +57,10 @@ npm install -g dev-memory-cli
|
|
|
61
57
|
# 在当前仓库安装生命周期 hook
|
|
62
58
|
dev-memory-cli install-hooks codex
|
|
63
59
|
dev-memory-cli install-hooks claude
|
|
60
|
+
dev-memory-cli install-hooks trae
|
|
61
|
+
dev-memory-cli install-hooks trae-cn
|
|
64
62
|
|
|
65
|
-
#
|
|
63
|
+
# 或一次安装全部支持的客户端
|
|
66
64
|
dev-memory-cli install-hooks --all
|
|
67
65
|
```
|
|
68
66
|
|
|
@@ -74,6 +72,8 @@ dev-memory-cli install-hooks --all --global
|
|
|
74
72
|
|
|
75
73
|
CLI 也可通过 `npx -y dev-memory-cli ...` 按需执行。Skill 与 hook 相互独立,安装 skill 不会修改本地 hook 配置。
|
|
76
74
|
|
|
75
|
+
Trae 的 Hook 配置按客户端隔离:国际版写入 `.trae/hooks.json`,国内版写入 `.trae-cn/hooks.json`;加 `--global` 时分别写入用户目录下的同名路径。已有 Hook 会保留,重复安装只更新 dev-memory 自己的条目。
|
|
76
|
+
|
|
77
77
|
Codex Desktop 没有本仓库依赖的项目生命周期 hook。安装每日扫描任务可覆盖 Codex CLI 和 Desktop 共同写入的本机会话文件:
|
|
78
78
|
|
|
79
79
|
```bash
|
|
@@ -85,27 +85,23 @@ dev-memory-cli session-scan status
|
|
|
85
85
|
|
|
86
86
|
## 基本使用
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
记忆是显式 opt-in。首次使用先初始化,然后由生命周期 hook 恢复、由定期 session-scan 沉淀新会话语义。
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
|
+
# 为当前 repo + branch 初始化记忆骨架
|
|
92
|
+
dev-memory-cli init
|
|
93
|
+
|
|
91
94
|
# 查看当前仓库和分支对应的记忆路径
|
|
92
95
|
dev-memory-cli read show
|
|
93
96
|
|
|
94
97
|
# 只在当前 repo 的记忆范围内搜索
|
|
95
98
|
dev-memory-cli read search --query "发布流程" --query "回滚"
|
|
96
99
|
|
|
97
|
-
#
|
|
98
|
-
dev-memory-cli
|
|
99
|
-
--kind decision \
|
|
100
|
-
--content "发布前必须先跑完整打包检查"
|
|
101
|
-
|
|
102
|
-
# 写入一条跨分支规则
|
|
103
|
-
dev-memory-cli capture record \
|
|
104
|
-
--kind shared-decision \
|
|
105
|
-
--content "Python 测试优先使用项目虚拟环境"
|
|
100
|
+
# 启动独立整理 Agent(不会把整理 prompt 放进普通开发会话)
|
|
101
|
+
dev-memory-cli maintain tidy
|
|
106
102
|
|
|
107
|
-
#
|
|
108
|
-
dev-memory-cli
|
|
103
|
+
# 启动独立归档 Agent
|
|
104
|
+
dev-memory-cli maintain archive --branch feature/example
|
|
109
105
|
```
|
|
110
106
|
|
|
111
107
|
`read search` 默认搜索当前 branch + repo 共享层。跨分支和归档范围通过 `--scope` 显式指定:
|
|
@@ -115,11 +111,11 @@ dev-memory-cli read search --scope all-branches --query "关键词"
|
|
|
115
111
|
dev-memory-cli read search --scope archived --query "关键词"
|
|
116
112
|
```
|
|
117
113
|
|
|
118
|
-
###
|
|
114
|
+
### 底层写入路由
|
|
119
115
|
|
|
120
116
|

|
|
121
117
|
|
|
122
|
-
`capture record` 支持三种输入方式:
|
|
118
|
+
`capture` 是 session-scan 和维护 Agent 使用的底层写入引擎,同时保留为高级管理 CLI;它不再对应常驻 Skill。`capture record` 支持三种输入方式:
|
|
123
119
|
|
|
124
120
|
- **显式 kind**:调用方明确指定内容语义,直接写目标 section。
|
|
125
121
|
- **自动分类**:`--auto` 根据文本信号判断 decision、risk、glossary;无法明确分类时进入 `unsorted.md`。
|
|
@@ -150,19 +146,18 @@ dev-memory-cli capture rewrite-entry --id <entry-id> --content "修正后的完
|
|
|
150
146
|
dev-memory-cli capture delete-entry --id <entry-id>
|
|
151
147
|
```
|
|
152
148
|
|
|
153
|
-
符合跨分支复用判定的内容会同时进入 `pending-promotion.md
|
|
149
|
+
符合跨分支复用判定的内容会同时进入 `pending-promotion.md`,由归档维护 Agent 在分支收尾阶段审核;候选内容不会直接写入 repo 共享层。
|
|
154
150
|
|
|
155
151
|
### 整理与归档
|
|
156
152
|
|
|
157
|
-
|
|
153
|
+
普通入口只有两个,都会启动独立交互式 Agent:
|
|
158
154
|
|
|
159
|
-
|
|
|
155
|
+
| 命令 | 处理对象 | 结果 |
|
|
160
156
|
| --- | --- | --- |
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
163
|
-
| `graduate` | 已完成分支的有效记忆 | 上提跨分支知识,并把分支目录移入 `_archived` |
|
|
157
|
+
| `dev-memory-cli maintain tidy` | `unsorted.md` 与已结构化但漂移的内容 | 分类、proposal HTML 审核、备份后 edit/delete/reset |
|
|
158
|
+
| `dev-memory-cli maintain archive` | 已完成分支的有效记忆 | dry-run、上提跨分支知识、确认后移入 `_archived` |
|
|
164
159
|
|
|
165
|
-
`tidy apply` 和 `graduate apply`
|
|
160
|
+
维护 Agent 内部使用 `setup`、`tidy`、`graduate` 等低层子命令。`tidy apply` 和 `graduate apply` 会改变或移动已有记忆,仍然受人工审核和显式确认门禁保护。用 `--print-prompt` 可以只查看将传给独立 Agent 的完整维护指令。
|
|
166
161
|
|
|
167
162
|
### 分支记忆操作
|
|
168
163
|
|
|
@@ -187,18 +182,18 @@ git config --local dev-memory.worktreeWriteback true
|
|
|
187
182
|
|
|
188
183
|
## 生命周期 Hook
|
|
189
184
|
|
|
190
|
-
| 事件 | Codex CLI | Claude Code CLI | 行为 |
|
|
191
|
-
| --- | :-: | :-: | --- |
|
|
192
|
-
| `SessionStart` | ✓ | ✓ | 刷新 Git 派生索引,注入浓缩记忆和完整文件路径;同一 session 幂等 |
|
|
193
|
-
| `Stop` | ✓ | ✓ | 记录轻量 HEAD marker
|
|
194
|
-
| `PreCompact` | | ✓ | 兼容占位,当前不执行额外刷新 |
|
|
195
|
-
| `SessionEnd` | | ✓ | 记录最终 HEAD,并把 transcript 总结任务放入后台队列 |
|
|
185
|
+
| 事件 | Codex CLI | Claude Code CLI | Trae | Trae CN | 行为 |
|
|
186
|
+
| --- | :-: | :-: | :-: | :-: | --- |
|
|
187
|
+
| `SessionStart` | ✓ | ✓ | ✓ | ✓ | 刷新 Git 派生索引,注入浓缩记忆和完整文件路径;同一 session 幂等 |
|
|
188
|
+
| `Stop` | ✓ | ✓ | ✓ | ✓ | 记录轻量 HEAD marker;有可识别会话信息时登记扫描候选,不启动模型 |
|
|
189
|
+
| `PreCompact` | | ✓ | | | 兼容占位,当前不执行额外刷新 |
|
|
190
|
+
| `SessionEnd` | | ✓ | | | 记录最终 HEAD,并把 transcript 总结任务放入后台队列 |
|
|
196
191
|
|
|
197
192
|
### 会话总结
|
|
198
193
|
|
|
199
|
-
Claude Code CLI 通过 `SessionEnd` 创建后台总结任务。Codex
|
|
194
|
+
Claude Code CLI 通过 `SessionEnd` 创建后台总结任务。Codex、Trae 和 Trae CN 当前没有该事件:它们的 `Stop` hook 只做轻量记录,并在 payload 带有可识别会话信息时登记扫描候选,不会在每轮回复后启动总结模型。现有定时扫描器只解析 Codex CLI / Desktop 写入的 `~/.codex/sessions` 和 `~/.codex/archived_sessions`;Trae Hook 支持当前覆盖 SessionStart 恢复与轻量 HEAD 刷新,不宣称扫描其私有会话存储。
|
|
200
195
|
|
|
201
|
-
任务触发端与总结执行端相互独立。Claude 的即时 worker 和 Codex 定时扫描器都可以使用 `coco`、`codex` 或 `claude` CLI。扫描器在 `~/.dev-memory/config.json` 的 `session_scan` 中内置三个可配置 preset,默认按 `
|
|
196
|
+
任务触发端与总结执行端相互独立。Claude 的即时 worker 和 Codex 定时扫描器都可以使用 `coco`、`codex` 或 `claude` CLI。扫描器在 `~/.dev-memory/config.json` 的 `session_scan` 中内置三个可配置 preset,默认按 `claude → codex` 选择第一个可用命令;`coco` preset 继续保留,可显式启用或加入顺序。每个 preset 可指定模型、profile、额外参数和环境变量。
|
|
202
197
|
|
|
203
198
|
总结输入包含全部尚未处理的 user/assistant 语义消息和现有 dev-memory,不按“最近几条”截尾,也不截断单条消息。长会话按顺序分块并最终归并;工具调用流水账、system 消息和 reasoning 不参与语义总结。输出限定为结构化 JSON,用于新增或修正 decisions、risks、glossary、file map 和 repo 共享记忆;时效性的“当前进展”“下一步”“当前阶段”不会写入。
|
|
204
199
|
|
|
@@ -251,7 +246,7 @@ dev-memory-cli workspace show
|
|
|
251
246
|
dev-memory-cli workspace primary <repo-basename>
|
|
252
247
|
```
|
|
253
248
|
|
|
254
|
-
`DEV_MEMORY_PRIMARY_REPO` 可作为临时 override
|
|
249
|
+
`DEV_MEMORY_PRIMARY_REPO` 可作为临时 override。维护命令在 workspace 中应使用 `--repo <repo-path-or-basename>` 明确目标仓库。
|
|
255
250
|
|
|
256
251
|
## 存储结构
|
|
257
252
|
|
|
@@ -327,6 +322,9 @@ dev-memory-cli context injection-preview \
|
|
|
327
322
|
|
|
328
323
|
```text
|
|
329
324
|
dev-memory-cli read <show|search>
|
|
325
|
+
dev-memory-cli init [--repo PATH] [--branch NAME]
|
|
326
|
+
dev-memory-cli maintain <tidy|archive> [--repo PATH] [--branch NAME] [--executor auto|codex|coco]
|
|
327
|
+
# low-level mutation/admin commands:
|
|
330
328
|
dev-memory-cli capture <show|suggest-kind|classify|record|list-entries|find-candidates|rewrite-entry|delete-entry|apply-summary-output|sync-working-tree|record-head>
|
|
331
329
|
dev-memory-cli setup <init|merge-unsorted|mark-completed>
|
|
332
330
|
dev-memory-cli tidy <prepare|apply>
|
|
@@ -338,7 +336,7 @@ dev-memory-cli summary <extract-core>
|
|
|
338
336
|
dev-memory-cli session-scan <run|replay|install|status|stats|history|show|uninstall|config>
|
|
339
337
|
dev-memory-cli hook <session-start|pre-compact|stop|session-end>
|
|
340
338
|
dev-memory-cli ui
|
|
341
|
-
dev-memory-cli install-hooks <codex|claude|--all>
|
|
339
|
+
dev-memory-cli install-hooks <codex|claude|trae|trae-cn|--all>
|
|
342
340
|
```
|
|
343
341
|
|
|
344
342
|
具体参数以各子命令的 `--help` 为准。
|
|
@@ -360,11 +358,12 @@ dev-memory 的能力边界不包括:
|
|
|
360
358
|
```text
|
|
361
359
|
bin/dev-memory.js # CLI 入口与 Node 侧命令
|
|
362
360
|
lib/dev_memory_*.py # 记忆、分支、会话扫描、整理与归档实现
|
|
361
|
+
lib/maintenance/ # 按需注入独立维护 Agent 的 prompt
|
|
363
362
|
lib/ui-server.js # 本地管理面板服务
|
|
364
363
|
lib/ui-app.html # 管理面板前端
|
|
365
364
|
scripts/hooks/ # 生命周期 hook 实现
|
|
366
|
-
hooks/ # Codex / Claude hook 模板
|
|
367
|
-
skills/
|
|
365
|
+
hooks/ # Codex / Claude Code / Trae hook 模板
|
|
366
|
+
skills/dev-memory-read/ # 唯一常驻 Skill
|
|
368
367
|
tests/ # Python 测试
|
|
369
368
|
docs/diagrams/ # README 图表及源文件
|
|
370
369
|
```
|
package/bin/dev-memory.js
CHANGED
|
@@ -9,6 +9,7 @@ const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
|
9
9
|
const DEFAULT_STORAGE_ROOT = path.join(os.homedir(), ".dev-memory", "repos");
|
|
10
10
|
const DEFAULT_CONFIG_PATH = process.env.DEV_MEMORY_CONFIG_PATH || path.join(os.homedir(), ".dev-memory", "config.json");
|
|
11
11
|
const WORKSPACE_CONFIG_NAME = ".dev-memory-workspace.json";
|
|
12
|
+
const SUPPORTED_HOOK_AGENTS = ["codex", "claude", "trae", "trae-cn"];
|
|
12
13
|
|
|
13
14
|
function fail(message) {
|
|
14
15
|
process.stderr.write(`ERROR: ${message}\n`);
|
|
@@ -191,32 +192,54 @@ function ensureSessionSummaryConfig() {
|
|
|
191
192
|
function templatePathForAgent(agent) {
|
|
192
193
|
if (agent === "codex") return packageScript("hooks", "codex-hooks.json");
|
|
193
194
|
if (agent === "claude") return packageScript("hooks", "hooks.json");
|
|
195
|
+
if (agent === "trae" || agent === "trae-cn") return packageScript("hooks", "trae-hooks.json");
|
|
194
196
|
fail(`unsupported agent: ${agent}`);
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
function targetPathForAgent(agent, repoRoot) {
|
|
198
200
|
if (agent === "codex") return path.join(repoRoot, ".codex", "hooks.json");
|
|
199
201
|
if (agent === "claude") return path.join(repoRoot, ".claude", "settings.local.json");
|
|
202
|
+
if (agent === "trae") return path.join(repoRoot, ".trae", "hooks.json");
|
|
203
|
+
if (agent === "trae-cn") return path.join(repoRoot, ".trae-cn", "hooks.json");
|
|
200
204
|
fail(`unsupported agent: ${agent}`);
|
|
201
205
|
}
|
|
202
206
|
|
|
203
207
|
function globalTargetPathForAgent(agent) {
|
|
204
208
|
if (agent === "codex") return path.join(os.homedir(), ".codex", "hooks.json");
|
|
205
209
|
if (agent === "claude") return path.join(os.homedir(), ".claude", "settings.json");
|
|
210
|
+
if (agent === "trae") return path.join(os.homedir(), ".trae", "hooks.json");
|
|
211
|
+
if (agent === "trae-cn") return path.join(os.homedir(), ".trae-cn", "hooks.json");
|
|
206
212
|
fail(`unsupported agent: ${agent}`);
|
|
207
213
|
}
|
|
208
214
|
|
|
215
|
+
function hookListIdentity(item) {
|
|
216
|
+
if (!item || typeof item !== "object") return null;
|
|
217
|
+
if (typeof item.id === "string" && item.id) {
|
|
218
|
+
return `id:${item.id}\u0000${item.matcher || ""}`;
|
|
219
|
+
}
|
|
220
|
+
const nestedHooks = Array.isArray(item.hooks) ? item.hooks : [];
|
|
221
|
+
for (const hook of nestedHooks) {
|
|
222
|
+
const command = hook && typeof hook.command === "string" ? hook.command.trim() : "";
|
|
223
|
+
const match = command.match(/(?:^|\s)dev-memory-cli\s+hook\s+(session-start|pre-compact|stop|session-end)(?:\s|$)/);
|
|
224
|
+
if (match) return `dev-memory-command:${match[1]}`;
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
209
229
|
function mergeHookLists(existingItems, incomingItems) {
|
|
210
230
|
const merged = existingItems.map((item) => ({ ...item }));
|
|
211
231
|
const index = new Map();
|
|
212
|
-
merged.forEach((item, i) =>
|
|
232
|
+
merged.forEach((item, i) => {
|
|
233
|
+
const key = hookListIdentity(item);
|
|
234
|
+
if (key) index.set(key, i);
|
|
235
|
+
});
|
|
213
236
|
for (const item of incomingItems) {
|
|
214
237
|
const copied = { ...item };
|
|
215
|
-
const key =
|
|
216
|
-
if (index.has(key)) {
|
|
238
|
+
const key = hookListIdentity(copied);
|
|
239
|
+
if (key && index.has(key)) {
|
|
217
240
|
merged[index.get(key)] = copied;
|
|
218
241
|
} else {
|
|
219
|
-
index.set(key, merged.length);
|
|
242
|
+
if (key) index.set(key, merged.length);
|
|
220
243
|
merged.push(copied);
|
|
221
244
|
}
|
|
222
245
|
}
|
|
@@ -224,7 +247,7 @@ function mergeHookLists(existingItems, incomingItems) {
|
|
|
224
247
|
}
|
|
225
248
|
|
|
226
249
|
function mergeConfig(existingConfig, templateConfig) {
|
|
227
|
-
const result = { ...existingConfig };
|
|
250
|
+
const result = { ...templateConfig, ...existingConfig };
|
|
228
251
|
const existingHooks = existingConfig.hooks || {};
|
|
229
252
|
const templateHooks = templateConfig.hooks || {};
|
|
230
253
|
const mergedHooks = {};
|
|
@@ -272,7 +295,7 @@ function installHooksForAgent(agent, options) {
|
|
|
272
295
|
|
|
273
296
|
function commandInstallHooks(positional, options) {
|
|
274
297
|
const isAll = Boolean(options.all);
|
|
275
|
-
const agents = isAll ?
|
|
298
|
+
const agents = isAll ? SUPPORTED_HOOK_AGENTS : [positional[0] || options.agent || "codex"];
|
|
276
299
|
const reports = agents.map((agent) => installHooksForAgent(agent, options));
|
|
277
300
|
process.stdout.write(`${JSON.stringify(isAll ? reports : reports[0], null, 2)}\n`);
|
|
278
301
|
}
|
|
@@ -376,6 +399,112 @@ function commandPySubcommand(name, rawArgs) {
|
|
|
376
399
|
runPython(scriptPath, rawArgs);
|
|
377
400
|
}
|
|
378
401
|
|
|
402
|
+
const MAINTENANCE_MODES = new Set(["tidy", "archive"]);
|
|
403
|
+
const MAINTENANCE_MARKER = "DEV_MEMORY_INTERNAL_MAINTENANCE_AGENT_V1";
|
|
404
|
+
|
|
405
|
+
function maintenanceHelp() {
|
|
406
|
+
process.stdout.write(`Usage:
|
|
407
|
+
dev-memory-cli maintain tidy [--repo PATH] [--branch NAME] [--scope branch|branch+repo]
|
|
408
|
+
[--executor auto|codex|coco] [--model MODEL]
|
|
409
|
+
dev-memory-cli maintain archive [--repo PATH] [--branch NAME]
|
|
410
|
+
[--executor auto|codex|coco] [--model MODEL]
|
|
411
|
+
dev-memory-cli maintain <tidy|archive> --print-prompt [other options]
|
|
412
|
+
|
|
413
|
+
The command starts a dedicated interactive maintenance-agent session. Tidy
|
|
414
|
+
must not apply destructive changes before HTML review; archive must not apply
|
|
415
|
+
before dry-run review and explicit confirmation.
|
|
416
|
+
`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function resolveMaintenanceExecutor(requested) {
|
|
420
|
+
const name = requested || process.env.DEV_MEMORY_MAINTENANCE_EXECUTOR || "auto";
|
|
421
|
+
if (!new Set(["auto", "codex", "coco"]).has(name)) {
|
|
422
|
+
fail(`unsupported maintenance executor: ${name}`);
|
|
423
|
+
}
|
|
424
|
+
if (name !== "auto") return name;
|
|
425
|
+
for (const candidate of ["codex", "coco"]) {
|
|
426
|
+
if (commandExists(candidate)) return candidate;
|
|
427
|
+
}
|
|
428
|
+
fail("no maintenance executor found; install codex/coco or use --print-prompt");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function buildMaintenancePrompt(mode, options, repoRoot) {
|
|
432
|
+
const promptPath = packageScript("lib", "maintenance", `${mode}.md`);
|
|
433
|
+
if (!fs.existsSync(promptPath)) fail(`missing maintenance prompt: ${promptPath}`);
|
|
434
|
+
const workflow = fs.readFileSync(promptPath, "utf8").trim();
|
|
435
|
+
const cliPath = path.resolve(process.argv[1]);
|
|
436
|
+
const branch = options.branch || "<current-git-branch>";
|
|
437
|
+
const scope = mode === "tidy" ? (options.scope || "branch") : "branch";
|
|
438
|
+
return `${MAINTENANCE_MARKER}
|
|
439
|
+
|
|
440
|
+
你是 dev-memory 的专用维护 Agent。本会话只处理下面指定仓库的记忆维护,不承担普通开发任务。
|
|
441
|
+
|
|
442
|
+
目标仓库:${repoRoot}
|
|
443
|
+
目标分支:${branch}
|
|
444
|
+
维护模式:${mode}
|
|
445
|
+
整理范围:${scope}
|
|
446
|
+
本次必须使用的 CLI:node ${JSON.stringify(cliPath)}
|
|
447
|
+
|
|
448
|
+
不要依赖全局 dev-memory capture/setup/tidy/graduate Skill;完整维护流程已经随本提示提供。
|
|
449
|
+
涉及删除、改写或归档时,必须遵守下面流程中的人工审核和确认门禁。
|
|
450
|
+
|
|
451
|
+
${workflow}
|
|
452
|
+
`;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function commandMaintain(rawArgs) {
|
|
456
|
+
const mode = rawArgs[0];
|
|
457
|
+
if (!mode || mode === "--help" || mode === "-h") {
|
|
458
|
+
maintenanceHelp();
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (!MAINTENANCE_MODES.has(mode)) {
|
|
462
|
+
fail(`unknown maintenance mode: ${mode}`);
|
|
463
|
+
}
|
|
464
|
+
const { positional, options } = parseArgs(rawArgs.slice(1));
|
|
465
|
+
if (positional.length) fail(`unexpected maintenance argument: ${positional[0]}`);
|
|
466
|
+
const repoRoot = path.resolve(options.repo || process.cwd());
|
|
467
|
+
if (!fs.existsSync(repoRoot) || !fs.statSync(repoRoot).isDirectory()) {
|
|
468
|
+
fail(`maintenance repo does not exist: ${repoRoot}`);
|
|
469
|
+
}
|
|
470
|
+
if (mode === "tidy" && options.scope && !new Set(["branch", "branch+repo"]).has(options.scope)) {
|
|
471
|
+
fail(`unsupported tidy scope: ${options.scope}`);
|
|
472
|
+
}
|
|
473
|
+
const prompt = buildMaintenancePrompt(mode, options, repoRoot);
|
|
474
|
+
if (options["print-prompt"] || options["dry-run"]) {
|
|
475
|
+
process.stdout.write(prompt);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const executor = resolveMaintenanceExecutor(options.executor);
|
|
480
|
+
let args;
|
|
481
|
+
if (executor === "codex") {
|
|
482
|
+
const modelArgs = options.model ? ["--model", String(options.model)] : [];
|
|
483
|
+
args = [
|
|
484
|
+
"-C", repoRoot,
|
|
485
|
+
"--sandbox", "danger-full-access",
|
|
486
|
+
"--ask-for-approval", "on-request",
|
|
487
|
+
"--dangerously-bypass-hook-trust",
|
|
488
|
+
...modelArgs,
|
|
489
|
+
prompt,
|
|
490
|
+
];
|
|
491
|
+
} else {
|
|
492
|
+
const modelArgs = options.model ? ["--config", `model=${String(options.model)}`] : [];
|
|
493
|
+
args = ["--permission-mode", "default", ...modelArgs, prompt];
|
|
494
|
+
}
|
|
495
|
+
const result = spawnSync(executor, args, {
|
|
496
|
+
cwd: repoRoot,
|
|
497
|
+
env: {
|
|
498
|
+
...process.env,
|
|
499
|
+
DEV_MEMORY_MAINTENANCE_AGENT: "1",
|
|
500
|
+
DEV_MEMORY_MAINTENANCE_MODE: mode,
|
|
501
|
+
},
|
|
502
|
+
stdio: "inherit",
|
|
503
|
+
});
|
|
504
|
+
if (result.error) fail(`unable to start ${executor}: ${result.error.message}`);
|
|
505
|
+
if (result.status !== 0) process.exit(result.status || 1);
|
|
506
|
+
}
|
|
507
|
+
|
|
379
508
|
function branchScript() {
|
|
380
509
|
return packageScript("lib", "dev_memory_branch.py");
|
|
381
510
|
}
|
|
@@ -688,12 +817,15 @@ function commandBranch(rawArgs) {
|
|
|
688
817
|
function printHelp() {
|
|
689
818
|
process.stdout.write(`Usage:
|
|
690
819
|
dev-memory-cli hook <session-start|pre-compact|stop|session-end> [--repo PATH]
|
|
691
|
-
dev-memory-cli install-hooks <codex|claude> [--repo PATH] [--global]
|
|
820
|
+
dev-memory-cli install-hooks <codex|claude|trae|trae-cn> [--repo PATH] [--global]
|
|
692
821
|
dev-memory-cli install-hooks --all [--repo PATH] [--global]
|
|
693
822
|
dev-memory-cli ui [--port N] [--host HOST] [--no-open] [--read-only]
|
|
694
823
|
dev-memory-cli workspace <show|primary> [...]
|
|
824
|
+
dev-memory-cli init [--repo PATH] [--branch NAME]
|
|
695
825
|
dev-memory-cli read <show|search> [...]
|
|
826
|
+
dev-memory-cli maintain <tidy|archive> [...] # starts a dedicated interactive agent
|
|
696
827
|
dev-memory-cli context <show|...> [...]
|
|
828
|
+
# Low-level mutation/admin commands used by session-scan and maintenance agents:
|
|
697
829
|
dev-memory-cli capture <record|show|sync-working-tree|record-head|suggest-kind|classify> [...]
|
|
698
830
|
dev-memory-cli setup <init|merge-unsorted|mark-completed> [...]
|
|
699
831
|
dev-memory-cli graduate <dry-run|apply|index> [...]
|
|
@@ -719,6 +851,14 @@ function main() {
|
|
|
719
851
|
commandPySubcommand(command, argv.slice(1));
|
|
720
852
|
return;
|
|
721
853
|
}
|
|
854
|
+
if (command === "init") {
|
|
855
|
+
commandPySubcommand("setup", ["init", ...argv.slice(1)]);
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
if (command === "maintain") {
|
|
859
|
+
commandMaintain(argv.slice(1));
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
722
862
|
if (command === "branch") {
|
|
723
863
|
commandBranch(argv.slice(1));
|
|
724
864
|
return;
|
package/hooks/README.md
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# Lifecycle Hooks
|
|
2
2
|
|
|
3
|
-
本项目使用 agent 生命周期 hook,不使用 Git hook。Claude Code 与
|
|
3
|
+
本项目使用 agent 生命周期 hook,不使用 Git hook。Claude Code、Codex、Trae 与 Trae CN 的可用事件和配置目录不同,模板分别维护。
|
|
4
4
|
|
|
5
5
|
## 当前仓库里的接入方式
|
|
6
6
|
|
|
7
7
|
- repo-local 配置位置:
|
|
8
8
|
- Claude: `.claude/settings.local.json`
|
|
9
9
|
- Codex: `.codex/hooks.json`
|
|
10
|
+
- Trae: `.trae/hooks.json`
|
|
11
|
+
- Trae CN: `.trae-cn/hooks.json`
|
|
12
|
+
- user-level 配置位置:
|
|
13
|
+
- Claude: `~/.claude/settings.json`
|
|
14
|
+
- Codex: `~/.codex/hooks.json`
|
|
15
|
+
- Trae: `~/.trae/hooks.json`
|
|
16
|
+
- Trae CN: `~/.trae-cn/hooks.json`
|
|
10
17
|
- 可复用模板:
|
|
11
18
|
- Claude: [hooks/hooks.json](hooks.json)
|
|
12
19
|
- Codex: [hooks/codex-hooks.json](codex-hooks.json)
|
|
13
|
-
-
|
|
20
|
+
- Trae / Trae CN: [hooks/trae-hooks.json](trae-hooks.json)
|
|
21
|
+
- `dev-memory-cli` 是所有配置共用的稳定执行入口
|
|
14
22
|
- hook 仅在模板内容合并到对应 agent 配置后生效
|
|
15
23
|
|
|
16
24
|
### Codex 快速安装
|
|
@@ -34,18 +42,21 @@ sh -c "$(curl -fsSL https://raw.githubusercontent.com/xluos/dev-memory-skill-sui
|
|
|
34
42
|
```bash
|
|
35
43
|
dev-memory-cli install-hooks codex
|
|
36
44
|
dev-memory-cli install-hooks claude
|
|
45
|
+
dev-memory-cli install-hooks trae
|
|
46
|
+
dev-memory-cli install-hooks trae-cn
|
|
47
|
+
dev-memory-cli install-hooks --all
|
|
37
48
|
```
|
|
38
49
|
|
|
39
50
|
已有旧配置如果还写着 `dev-memory hook ...` 或 `npx dev-memory hook ...`,重新执行安装命令会按 hook id 覆盖为 `dev-memory-cli hook ...`。
|
|
40
51
|
|
|
41
52
|
## Hook 行为
|
|
42
53
|
|
|
43
|
-
| 事件 | Codex CLI | Claude Code CLI | 行为 |
|
|
44
|
-
| --- | :-: | :-: | --- |
|
|
45
|
-
| `SessionStart` | ✓ | ✓ | 读取当前 repo+branch 记忆并注入会话;同一 session 的重复触发幂等跳过 |
|
|
46
|
-
| `Stop` | ✓ | ✓ | 每次回复后记录轻量 HEAD marker;
|
|
47
|
-
| `PreCompact` | | ✓ | 兼容占位;当前不执行额外刷新 |
|
|
48
|
-
| `SessionEnd` | | ✓ | 记录最终 HEAD,创建 transcript 总结任务并启动后台 worker |
|
|
54
|
+
| 事件 | Codex CLI | Claude Code CLI | Trae | Trae CN | 行为 |
|
|
55
|
+
| --- | :-: | :-: | :-: | :-: | --- |
|
|
56
|
+
| `SessionStart` | ✓ | ✓ | ✓ | ✓ | 读取当前 repo+branch 记忆并注入会话;同一 session 的重复触发幂等跳过 |
|
|
57
|
+
| `Stop` | ✓ | ✓ | ✓ | ✓ | 每次回复后记录轻量 HEAD marker;payload 有会话信息时登记扫描候选 |
|
|
58
|
+
| `PreCompact` | | ✓ | | | 兼容占位;当前不执行额外刷新 |
|
|
59
|
+
| `SessionEnd` | | ✓ | | | 记录最终 HEAD,创建 transcript 总结任务并启动后台 worker |
|
|
49
60
|
|
|
50
61
|
`SessionStart` 的幂等记录位于 `<repo-memory>/jobs/session-start/injected/*.json`。重复触发只记录 skip 日志,不重复注入上下文。
|
|
51
62
|
|
|
@@ -53,7 +64,7 @@ dev-memory-cli install-hooks claude
|
|
|
53
64
|
|
|
54
65
|
### 生效范围
|
|
55
66
|
|
|
56
|
-
`SessionEnd` 自动总结仅由 **Claude Code CLI** 的生命周期事件触发。Codex
|
|
67
|
+
`SessionEnd` 自动总结仅由 **Claude Code CLI** 的生命周期事件触发。Codex、Trae 和 Trae CN 模板都不注册 `SessionEnd`;它们的 `Stop` 只做轻量记录,不在每轮回答后启动模型。Codex Desktop 不使用这组项目 hook。
|
|
57
68
|
|
|
58
69
|
总结任务的触发端与执行端是两个独立概念:
|
|
59
70
|
|
|
@@ -138,13 +149,13 @@ dev-memory-cli session-scan status
|
|
|
138
149
|
|
|
139
150
|
### 执行器配置
|
|
140
151
|
|
|
141
|
-
`~/.dev-memory/config.json` 中的 `session_scan` 独立配置扫描执行器。默认提供 `coco`、`codex`、`claude` 三个 preset
|
|
152
|
+
`~/.dev-memory/config.json` 中的 `session_scan` 独立配置扫描执行器。默认提供 `coco`、`codex`、`claude` 三个 preset,并按 `claude → codex` 选择第一个已安装且启用的命令;`coco` preset 为兼容保留,可显式加入 `order`:
|
|
142
153
|
|
|
143
154
|
```json
|
|
144
155
|
{
|
|
145
156
|
"session_scan": {
|
|
146
157
|
"executor": "auto",
|
|
147
|
-
"order": ["
|
|
158
|
+
"order": ["claude", "codex"],
|
|
148
159
|
"schedule_times": ["03:00", "13:00"],
|
|
149
160
|
"skip_when_computer_active": true,
|
|
150
161
|
"active_within_minutes": 10,
|
|
@@ -214,6 +225,8 @@ dev-memory-cli session-scan uninstall
|
|
|
214
225
|
|
|
215
226
|
- Claude 把 [hooks/hooks.json](hooks.json) 合并到 `.claude/settings.local.json`。
|
|
216
227
|
- Codex CLI 把 [hooks/codex-hooks.json](codex-hooks.json) 合并到 `.codex/hooks.json`。
|
|
228
|
+
- Trae 把 [hooks/trae-hooks.json](trae-hooks.json) 合并到 `.trae/hooks.json`。
|
|
229
|
+
- Trae CN 把同一模板合并到 `.trae-cn/hooks.json`;模板声明 Trae 要求的 `version: 1`。
|
|
217
230
|
- Codex Desktop 通过本地 rollout 定时扫描覆盖,不依赖项目 hook。
|
|
218
231
|
- 其他仓库需要先安装 CLI,再安装对应 hook;hook 运行时统一调用 `dev-memory-cli hook ...`。
|
|
219
232
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"hooks": {
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "dev-memory-cli hook session-start",
|
|
10
|
+
"timeout": 20,
|
|
11
|
+
"statusMessage": "Loading dev-memory context"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"Stop": [
|
|
17
|
+
{
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "dev-memory-cli hook stop",
|
|
22
|
+
"timeout": 15,
|
|
23
|
+
"statusMessage": "Updating dev-memory session marker"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -29,6 +29,8 @@ PLIST_PATH = Path(os.environ.get(
|
|
|
29
29
|
"~/Library/LaunchAgents/com.dev-memory.session-scan.plist",
|
|
30
30
|
)).expanduser()
|
|
31
31
|
INTERNAL_MARKER = "DEV_MEMORY_INTERNAL_SESSION_SUMMARY_V1"
|
|
32
|
+
MAINTENANCE_MARKER = "DEV_MEMORY_INTERNAL_MAINTENANCE_AGENT_V1"
|
|
33
|
+
INTERNAL_MARKERS = (INTERNAL_MARKER, MAINTENANCE_MARKER)
|
|
32
34
|
SUMMARY_MUTATION_FIELDS = (
|
|
33
35
|
"file_map",
|
|
34
36
|
"decisions",
|
|
@@ -124,7 +126,7 @@ def _deep_merge(base, override):
|
|
|
124
126
|
def default_scan_config():
|
|
125
127
|
return {
|
|
126
128
|
"executor": "auto",
|
|
127
|
-
"order": ["
|
|
129
|
+
"order": ["claude", "codex"],
|
|
128
130
|
"executors": json.loads(json.dumps(DEFAULT_EXECUTORS)),
|
|
129
131
|
"schedule_times": ["03:00", "13:00"],
|
|
130
132
|
"skip_when_computer_active": True,
|
|
@@ -360,7 +362,7 @@ def parse_codex_session(path, since_offset=0):
|
|
|
360
362
|
if usage:
|
|
361
363
|
total_usage = usage
|
|
362
364
|
message = _semantic_message(obj)
|
|
363
|
-
if message and
|
|
365
|
+
if message and any(marker in message["text"] for marker in INTERNAL_MARKERS):
|
|
364
366
|
internal_marker = True
|
|
365
367
|
if message and end > since_offset:
|
|
366
368
|
message.update({"start_offset": start, "end_offset": end})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# 分支记忆归档维护流程
|
|
2
|
+
|
|
3
|
+
目标是把已完成分支中真正跨分支复用的知识提炼到 repo 共享层,然后归档该分支的记忆目录。归档是破坏性显式动作,必须由用户确认。
|
|
4
|
+
|
|
5
|
+
## 硬规则
|
|
6
|
+
|
|
7
|
+
- 始终只处理提示中指定的 repo + branch。
|
|
8
|
+
- 所有命令都使用提示中给出的 CLI 路径,并显式传 `--repo`、必要时传 `--branch`。
|
|
9
|
+
- 禁止跳过 dry-run 直接 apply。
|
|
10
|
+
- 当前分支存在未提交改动、领先默认基线且尚未 merge,或仍在开发时,不得擅自归档;先向用户说明并确认。
|
|
11
|
+
- pending-promotion 只是候选,不是必选。业务专用事实不得上提到 repo 共享层。
|
|
12
|
+
- 上提决策必须保留 Why 与影响范围,并改写成脱离当前分支名称仍可理解的最终事实。
|
|
13
|
+
|
|
14
|
+
## 第一阶段:pre-flight
|
|
15
|
+
|
|
16
|
+
运行:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
<CLI> graduate dry-run --repo <REPO> [--branch <BRANCH>]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
检查并向用户报告:
|
|
23
|
+
|
|
24
|
+
- `git_status.ahead`
|
|
25
|
+
- `git_status.uncommitted`
|
|
26
|
+
- 默认基线与当前分支
|
|
27
|
+
- `archive_destination`
|
|
28
|
+
- 主审核面 `primary_sources.pending-promotion.md`
|
|
29
|
+
- 主审核面 `primary_sources.decisions.md`
|
|
30
|
+
- 其他 `cross_check_sources` 中可能遗漏的跨分支经验
|
|
31
|
+
|
|
32
|
+
若 pre-flight 暴露未 merge 或未提交状态,停止在确认点,不要继续生成 apply。
|
|
33
|
+
|
|
34
|
+
## 第二阶段:生成 harvest 草案
|
|
35
|
+
|
|
36
|
+
只提炼真正跨分支稳定成立的内容,生成:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"repo_overview": [
|
|
41
|
+
{"section": "长期目标与边界", "body": "...", "mode": "append"}
|
|
42
|
+
],
|
|
43
|
+
"repo_decisions": [
|
|
44
|
+
{"section": "跨分支通用决策", "body": "...", "mode": "append"}
|
|
45
|
+
],
|
|
46
|
+
"repo_glossary": [
|
|
47
|
+
{"section": "长期有效背景", "body": "...", "mode": "append"},
|
|
48
|
+
{"section": "共享入口", "body": "...", "mode": "append"}
|
|
49
|
+
],
|
|
50
|
+
"notes": "从目标分支提炼",
|
|
51
|
+
"archive": true
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
提炼时:
|
|
56
|
+
|
|
57
|
+
- 去掉当前需求名、临时人员分工、一次性状态和提交流水。
|
|
58
|
+
- 保留通用约束、反直觉风险、长期资料入口和可复用流程。
|
|
59
|
+
- 对照现有 repo 共享记忆去重;旧共享结论失效时应改成最终口径,不能制造两条冲突规则。
|
|
60
|
+
- 默认 `mode=append`,除非已经确认需要更新已有 section。
|
|
61
|
+
|
|
62
|
+
把 harvest 草案完整展示给用户,包括“会上提什么”和“只归档、不做上提什么”。获得明确确认之前禁止 apply。
|
|
63
|
+
|
|
64
|
+
## 第三阶段:apply
|
|
65
|
+
|
|
66
|
+
用户确认草案与归档目标后运行:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
<CLI> graduate apply --repo <REPO> [--branch <BRANCH>] --harvest-file <HARVEST>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
apply 会串行写入 repo 共享层,生成 `archive_summary.md`,并把分支目录移动到 `branches/_archived/<branch>__<date>/`,同时更新 `_archived/INDEX.md`。
|
|
73
|
+
|
|
74
|
+
## 第四阶段:复核
|
|
75
|
+
|
|
76
|
+
完成后必须:
|
|
77
|
+
|
|
78
|
+
1. 读取 `archive_summary.md`。
|
|
79
|
+
2. 运行 `<CLI> graduate index --repo <REPO>` 确认归档索引存在。
|
|
80
|
+
3. 检查原 branch_dir 已移动、archive destination 存在。
|
|
81
|
+
4. 汇总实际上提条目、归档路径和任何跳过项。
|
|
82
|
+
|
|
83
|
+
如果 apply 报 branch 不存在、schema 漂移或并发归档冲突,停止并报告真实错误;不得强行移动目录或手写 INDEX 绕过 CLI。
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# 记忆整理维护流程
|
|
2
|
+
|
|
3
|
+
目标是把未分类内容归位,并校准已经结构化但陈旧、重复、错误或残留模板的记忆。整理是显式维护任务,不在普通开发会话中自动触发。
|
|
4
|
+
|
|
5
|
+
## 硬规则
|
|
6
|
+
|
|
7
|
+
- 始终只处理提示中指定的单个 repo + branch。
|
|
8
|
+
- 所有命令都使用提示中给出的 CLI 路径,并显式传 `--repo`、必要时传 `--branch`。
|
|
9
|
+
- 未分类内容可以在用户确认分类后 merge;结构化内容的删除和改写必须先生成 HTML review,由用户审核并导出 plan,禁止直接 apply。
|
|
10
|
+
- 不复制 PRD、聊天流水或 Git 历史;只保留下次开发仍有价值的决策、风险、术语、约束、入口与文件导航。
|
|
11
|
+
- `progress.md` 的 `AUTO-GENERATED` 块不得删除或改写。
|
|
12
|
+
|
|
13
|
+
## 第一阶段:处理 unsorted
|
|
14
|
+
|
|
15
|
+
先运行:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
<CLI> setup init --repo <REPO> [--branch <BRANCH>]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
如果 `unsorted_entries` 非空:
|
|
22
|
+
|
|
23
|
+
1. 把相关条目聚合后给出高置信度分类建议。
|
|
24
|
+
2. 可选 kind:`decision`、`risk`、`glossary`、`source`、`shared-decision`、`shared-context`、`shared-source`、`skip`。
|
|
25
|
+
3. 让用户确认或修正分类;未确认前不要 merge。
|
|
26
|
+
4. 生成 `classifications` plan,并运行 `setup merge-unsorted --plan-file ...`。
|
|
27
|
+
|
|
28
|
+
如果 unsorted 为空,不需要为了修改 `setup_completed` 制造额外步骤;继续结构化整理即可。
|
|
29
|
+
|
|
30
|
+
## 第二阶段:扫描结构化记忆
|
|
31
|
+
|
|
32
|
+
运行:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
<CLI> tidy prepare --repo <REPO> [--branch <BRANCH>] --scope <SCOPE>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`SCOPE` 使用启动提示中的 `branch` 或 `branch+repo`。第一次 prepare 后优先读取输出中的 `annotated_md`,不要只看 stdout 的扁平 entries。它保留了:
|
|
39
|
+
|
|
40
|
+
- entry id
|
|
41
|
+
- block 边界
|
|
42
|
+
- section 结构
|
|
43
|
+
- Why/How 等 orphan paragraph
|
|
44
|
+
- STALE / ORPHAN hints
|
|
45
|
+
|
|
46
|
+
## 第三阶段:生成事项级 proposals
|
|
47
|
+
|
|
48
|
+
把相关条目聚合成约 3~8 个 proposal,每个 proposal 必须包含 `id`、`priority`、`title`、`reason`、`actions`。优先使用粗粒度动作:
|
|
49
|
+
|
|
50
|
+
1. `reset-file`
|
|
51
|
+
2. `delete-section`
|
|
52
|
+
3. `delete-block`
|
|
53
|
+
4. `delete-entries`
|
|
54
|
+
5. `edit-entries`
|
|
55
|
+
|
|
56
|
+
不要把每个 entry 单独做成 proposal。同一原因、同一结果的动作应合并。删除 block 时尽量附带 prepare 输出的 `expected_content_hash`,防止用户中途修改文件后误删。
|
|
57
|
+
|
|
58
|
+
priority 口径:
|
|
59
|
+
|
|
60
|
+
- `P0`:继续保留会误导下一次开发
|
|
61
|
+
- `P1`:强烈建议清理
|
|
62
|
+
- `P2`:建议清理
|
|
63
|
+
- `P3`:可选
|
|
64
|
+
- `P4`:可有可无
|
|
65
|
+
|
|
66
|
+
把 proposals 写入临时 JSON,再次运行:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
<CLI> tidy prepare --repo <REPO> [--branch <BRANCH>] --scope <SCOPE> --proposals-file <FILE>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 第四阶段:人工审核门禁
|
|
73
|
+
|
|
74
|
+
把 `review_html` / `open_url` 告诉用户,引导用户逐个选择:
|
|
75
|
+
|
|
76
|
+
- accept:按原方案执行
|
|
77
|
+
- reject:保持不动
|
|
78
|
+
- custom:给出新的处理意见
|
|
79
|
+
|
|
80
|
+
用户审核后从 HTML 导出 plan.json,并把路径交回本会话。没有用户导出的 plan 文件,禁止调用 `tidy apply`。
|
|
81
|
+
|
|
82
|
+
## 第五阶段:apply 与复核
|
|
83
|
+
|
|
84
|
+
用户明确提供已审核 plan 后运行:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
<CLI> tidy apply --repo <REPO> [--branch <BRANCH>] --plan-file <PLAN>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
apply 会先备份,再按 reset → section → block → entry 的顺序执行。完成后必须读取生成的 `summary_*.md`,核对 accepted/rejected/custom、实际 rewritten/deleted 和 invalid actions。
|
|
91
|
+
|
|
92
|
+
custom proposal 不会自动 apply。根据反馈决定是直接精确改写、解释后再确认、保持不动,还是重新生成 proposals;不要机械重复整轮 prepare。
|
|
93
|
+
|
|
94
|
+
## 完成输出
|
|
95
|
+
|
|
96
|
+
最终向用户说明:
|
|
97
|
+
|
|
98
|
+
- unsorted 分类合并了多少条
|
|
99
|
+
- HTML 审核了多少个 proposal
|
|
100
|
+
- 实际改写、删除、重置的数量
|
|
101
|
+
- 备份和 summary 路径
|
|
102
|
+
- 因漂移校验而跳过的动作
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dev-memory-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "CLI for dev-memory hooks and repo-local setup (formerly @xluos/dev-assets-cli)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"hooks/*.json",
|
|
29
29
|
"hooks/README.md",
|
|
30
30
|
"lib/*.py",
|
|
31
|
+
"lib/maintenance/*.md",
|
|
31
32
|
"lib/ui-server.js",
|
|
32
33
|
"lib/ui-app.html",
|
|
33
34
|
"lib/assets/*",
|
package/scripts/hooks/_common.py
CHANGED
|
@@ -490,13 +490,12 @@ def _extract_sections(paths, keys, *, repo_newest_first=False):
|
|
|
490
490
|
|
|
491
491
|
def _build_context_from_assets(assets, *, full=True, heading=None, brief_profile="standard"):
|
|
492
492
|
if not assets["branch_dir"].exists():
|
|
493
|
-
#
|
|
494
|
-
#
|
|
495
|
-
# write has happened yet — no need to push setup.
|
|
493
|
+
# Memory is opt-in. A missing branch directory stays silent in
|
|
494
|
+
# workspace briefs; the primary repo gets a concise init hint.
|
|
496
495
|
if heading is None:
|
|
497
496
|
return (
|
|
498
497
|
"当前仓库+分支还没有 dev-memory 记忆。"
|
|
499
|
-
"
|
|
498
|
+
"需要启用时运行 `dev-memory-cli init`;之后由定期 session-scan 沉淀会话语义。"
|
|
500
499
|
)
|
|
501
500
|
return None
|
|
502
501
|
|
|
@@ -617,7 +616,7 @@ def _build_context_from_assets(assets, *, full=True, heading=None, brief_profile
|
|
|
617
616
|
"",
|
|
618
617
|
f"归档分支查询:`grep -r 'KEYWORD' {archive_dir}/` (体量大时派 Task 子 agent)",
|
|
619
618
|
])
|
|
620
|
-
footer_lines.extend(["", "
|
|
619
|
+
footer_lines.extend(["", "新增会话语义由定期 session-scan 沉淀;主动查旧记忆使用 `dev-memory-read`。"])
|
|
621
620
|
parts.append("\n".join(footer_lines))
|
|
622
621
|
|
|
623
622
|
return "\n\n".join(parts)
|
package/suite-manifest.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"suite_name": "dev-memory-skill-suite",
|
|
3
3
|
"skills": [
|
|
4
|
-
"dev-memory-read"
|
|
4
|
+
"dev-memory-read"
|
|
5
|
+
],
|
|
6
|
+
"legacy_skills": [
|
|
5
7
|
"dev-memory-setup",
|
|
6
8
|
"dev-memory-capture",
|
|
7
9
|
"dev-memory-graduate",
|
|
8
|
-
"dev-memory-tidy"
|
|
9
|
-
],
|
|
10
|
-
"legacy_skills": [
|
|
10
|
+
"dev-memory-tidy",
|
|
11
11
|
"branch-context",
|
|
12
12
|
"branch-context-setup",
|
|
13
13
|
"dev-asset-setup",
|