@yun-zero/claw-memory 0.1.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.
Files changed (131) hide show
  1. package/.claude/settings.local.json +68 -0
  2. package/README.md +323 -0
  3. package/dist/config/llm.d.ts +13 -0
  4. package/dist/config/llm.d.ts.map +1 -0
  5. package/dist/config/llm.js +96 -0
  6. package/dist/config/llm.js.map +1 -0
  7. package/dist/config/plugin.d.ts +15 -0
  8. package/dist/config/plugin.d.ts.map +1 -0
  9. package/dist/config/plugin.js +32 -0
  10. package/dist/config/plugin.js.map +1 -0
  11. package/dist/db/entityRepository.d.ts +21 -0
  12. package/dist/db/entityRepository.d.ts.map +1 -0
  13. package/dist/db/entityRepository.js +55 -0
  14. package/dist/db/entityRepository.js.map +1 -0
  15. package/dist/db/repository.d.ts +22 -0
  16. package/dist/db/repository.d.ts.map +1 -0
  17. package/dist/db/repository.js +77 -0
  18. package/dist/db/repository.js.map +1 -0
  19. package/dist/db/schema.d.ts +5 -0
  20. package/dist/db/schema.d.ts.map +1 -0
  21. package/dist/db/schema.js +112 -0
  22. package/dist/db/schema.js.map +1 -0
  23. package/dist/db/todoRepository.d.ts +26 -0
  24. package/dist/db/todoRepository.d.ts.map +1 -0
  25. package/dist/db/todoRepository.js +54 -0
  26. package/dist/db/todoRepository.js.map +1 -0
  27. package/dist/hooks/bootstrap.d.ts +3 -0
  28. package/dist/hooks/bootstrap.d.ts.map +1 -0
  29. package/dist/hooks/bootstrap.js +28 -0
  30. package/dist/hooks/bootstrap.js.map +1 -0
  31. package/dist/hooks/message.d.ts +18 -0
  32. package/dist/hooks/message.d.ts.map +1 -0
  33. package/dist/hooks/message.js +52 -0
  34. package/dist/hooks/message.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +46 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/mcp/tools.d.ts +26 -0
  40. package/dist/mcp/tools.d.ts.map +1 -0
  41. package/dist/mcp/tools.js +360 -0
  42. package/dist/mcp/tools.js.map +1 -0
  43. package/dist/plugin.d.ts +18 -0
  44. package/dist/plugin.d.ts.map +1 -0
  45. package/dist/plugin.js +62 -0
  46. package/dist/plugin.js.map +1 -0
  47. package/dist/services/entityGraphService.d.ts +87 -0
  48. package/dist/services/entityGraphService.d.ts.map +1 -0
  49. package/dist/services/entityGraphService.js +271 -0
  50. package/dist/services/entityGraphService.js.map +1 -0
  51. package/dist/services/memory.d.ts +26 -0
  52. package/dist/services/memory.d.ts.map +1 -0
  53. package/dist/services/memory.js +281 -0
  54. package/dist/services/memory.js.map +1 -0
  55. package/dist/services/memoryIndex.d.ts +34 -0
  56. package/dist/services/memoryIndex.d.ts.map +1 -0
  57. package/dist/services/memoryIndex.js +100 -0
  58. package/dist/services/memoryIndex.js.map +1 -0
  59. package/dist/services/metadataExtractor.d.ts +16 -0
  60. package/dist/services/metadataExtractor.d.ts.map +1 -0
  61. package/dist/services/metadataExtractor.js +75 -0
  62. package/dist/services/metadataExtractor.js.map +1 -0
  63. package/dist/services/retrieval.d.ts +24 -0
  64. package/dist/services/retrieval.d.ts.map +1 -0
  65. package/dist/services/retrieval.js +40 -0
  66. package/dist/services/retrieval.js.map +1 -0
  67. package/dist/services/scheduler.d.ts +122 -0
  68. package/dist/services/scheduler.d.ts.map +1 -0
  69. package/dist/services/scheduler.js +434 -0
  70. package/dist/services/scheduler.js.map +1 -0
  71. package/dist/services/summarizer.d.ts +43 -0
  72. package/dist/services/summarizer.d.ts.map +1 -0
  73. package/dist/services/summarizer.js +252 -0
  74. package/dist/services/summarizer.js.map +1 -0
  75. package/dist/services/tagService.d.ts +64 -0
  76. package/dist/services/tagService.d.ts.map +1 -0
  77. package/dist/services/tagService.js +281 -0
  78. package/dist/services/tagService.js.map +1 -0
  79. package/dist/tools/memory.d.ts +3 -0
  80. package/dist/tools/memory.d.ts.map +1 -0
  81. package/dist/tools/memory.js +114 -0
  82. package/dist/tools/memory.js.map +1 -0
  83. package/dist/types.d.ts +128 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +6 -0
  86. package/dist/types.js.map +1 -0
  87. package/docs/plans/2026-03-02-claw-memory-design.md +445 -0
  88. package/docs/plans/2026-03-02-incremental-summary-design.md +157 -0
  89. package/docs/plans/2026-03-02-incremental-summary-implementation.md +468 -0
  90. package/docs/plans/2026-03-02-memory-index-design.md +163 -0
  91. package/docs/plans/2026-03-02-memory-index-implementation.md +836 -0
  92. package/docs/plans/2026-03-02-mvp-implementation.md +1703 -0
  93. package/docs/plans/2026-03-02-testing-implementation.md +395 -0
  94. package/docs/plans/2026-03-02-testing-plan.md +93 -0
  95. package/docs/plans/2026-03-03-claw-memory-openclaw-plugin-design.md +285 -0
  96. package/docs/plans/2026-03-03-claw-memory-plugin-implementation.md +642 -0
  97. package/docs/plans/2026-03-03-entity-graph-design.md +121 -0
  98. package/docs/plans/2026-03-03-entity-graph-implementation.md +687 -0
  99. package/docs/plans/2026-03-03-llm-generic-config-design.md +43 -0
  100. package/docs/plans/2026-03-03-llm-generic-config-implementation.md +186 -0
  101. package/docs/plans/2026-03-03-memory-e2e-stress-test-design.md +110 -0
  102. package/docs/plans/2026-03-03-memory-e2e-stress-test-implementation.md +464 -0
  103. package/docs/plans/2026-03-03-minimax-llm-fix.md +156 -0
  104. package/docs/plans/2026-03-03-scheduler-design.md +165 -0
  105. package/docs/plans/2026-03-03-scheduler-implementation.md +777 -0
  106. package/docs/plans/2026-03-03-tags-visualization-design.md +73 -0
  107. package/docs/plans/2026-03-03-tags-visualization-implementation.md +539 -0
  108. package/openclaw.plugin.json +11 -0
  109. package/package.json +41 -0
  110. package/src/config/llm.ts +129 -0
  111. package/src/config/plugin.ts +47 -0
  112. package/src/db/entityRepository.ts +80 -0
  113. package/src/db/repository.ts +106 -0
  114. package/src/db/schema.ts +121 -0
  115. package/src/db/todoRepository.ts +76 -0
  116. package/src/hooks/bootstrap.ts +36 -0
  117. package/src/hooks/message.ts +84 -0
  118. package/src/index.ts +50 -0
  119. package/src/plugin.ts +85 -0
  120. package/src/services/entityGraphService.ts +367 -0
  121. package/src/services/memory.ts +338 -0
  122. package/src/services/memoryIndex.ts +140 -0
  123. package/src/services/metadataExtractor.ts +89 -0
  124. package/src/services/retrieval.ts +71 -0
  125. package/src/services/scheduler.ts +529 -0
  126. package/src/services/summarizer.ts +318 -0
  127. package/src/services/tagService.ts +335 -0
  128. package/src/tools/memory.ts +137 -0
  129. package/src/types.ts +139 -0
  130. package/tsconfig.json +20 -0
  131. package/vitest.config.ts +16 -0
@@ -0,0 +1,73 @@
1
+ # 层级标签管理工具设计
2
+
3
+ ## 概述
4
+
5
+ 为 Claw-Memory 实现层级标签可视化管理工具,生成静态 HTML 报告展示标签树形结构和统计信息。
6
+
7
+ ## 需求
8
+
9
+ 1. CLI 命令生成标签树和统计 HTML 报告
10
+ 2. 标签树支持折叠展开
11
+ 3. 显示标签使用统计、层级分布、最近使用
12
+
13
+ ## CLI 命令设计
14
+
15
+ ```bash
16
+ # 生成标签树 HTML 报告(默认 output: tags-tree.html)
17
+ claw-memory tags tree [--output <file>]
18
+
19
+ # 生成标签统计 HTML 报告(默认 output: tags-stats.html)
20
+ claw-memory tags stats [--output <file>]
21
+ ```
22
+
23
+ ## 标签树 HTML 结构
24
+
25
+ ```html
26
+ <!-- 可折叠的树形结构 -->
27
+ <div class="tag-tree">
28
+ <div class="tag-item" data-level="0">
29
+ <span class="tag-toggle">▶</span>
30
+ <span class="tag-name">技术</span>
31
+ <span class="tag-count">(15条记忆, 20次使用)</span>
32
+ <div class="tag-children">
33
+ <!-- 子标签 -->
34
+ </div>
35
+ </div>
36
+ </div>
37
+ ```
38
+
39
+ ## 标签统计 HTML 结构
40
+
41
+ ```html
42
+ <!-- 使用频率柱状图 -->
43
+ <div class="chart-usage">
44
+ <div class="bar" style="width: 80%">技术 (20)</div>
45
+ </div>
46
+
47
+ <!-- 层级分布饼图 -->
48
+ <div class="chart-levels">
49
+ <div class="pie-segment" data-level="0">Level 0: 10</div>
50
+ </div>
51
+
52
+ <!-- 最近使用列表 -->
53
+ <div class="recent-tags">
54
+ <li>React - 2026-03-03</li>
55
+ </div>
56
+ ```
57
+
58
+ ## 数据来源
59
+
60
+ 使用现有的 EntityRepository 查询:
61
+ - `type = 'tag'` 的实体
62
+ - 通过 `parent_id` 构建层级关系
63
+ - 聚合 `memory_entities` 计算使用次数
64
+
65
+ ## 实现方式
66
+
67
+ - 使用现有 `EntityRepository` 查询数据
68
+ - 纯 HTML + 内联 CSS + 少量 JS
69
+ - 单文件输出,无需服务器
70
+
71
+ ## 新增依赖
72
+
73
+ 无(使用 Node.js 内置功能)
@@ -0,0 +1,539 @@
1
+ # 层级标签可视化实现计划
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** 实现 CLI 命令生成静态 HTML 报告,展示标签树形结构和统计信息
6
+
7
+ **Architecture:** 使用现有 EntityRepository 查询标签数据,生成内联 CSS + 少量 JS 的单文件 HTML
8
+
9
+ **Tech Stack:** TypeScript, Node.js 内置功能(无新依赖)
10
+
11
+ ---
12
+
13
+ ## 准备工作
14
+
15
+ ### Task 1: 创建开发分支
16
+
17
+ **Step 1: 创建并切换到新分支**
18
+
19
+ ```bash
20
+ cd /home/ubuntu/openclaw/claw-memory
21
+ git checkout -b feature/tags-visualization
22
+ ```
23
+
24
+ **Step 2: 验证分支**
25
+
26
+ ```bash
27
+ git branch --show-current
28
+ ```
29
+
30
+ Expected: `feature/tags-visualization`
31
+
32
+ ---
33
+
34
+ ## Task 2: 读取现有 EntityRepository
35
+
36
+ **Files:**
37
+ - Read: `src/db/entityRepository.ts`
38
+
39
+ **Step 1: 查看现有方法**
40
+
41
+ ```bash
42
+ cat src/db/entityRepository.ts
43
+ ```
44
+
45
+ 了解现有的查询方法,特别是:
46
+ - `findChildren(parentId)` - 查询子实体
47
+ - `findByType(type)` - 按类型查询
48
+
49
+ ---
50
+
51
+ ## Task 3: 创建标签服务类
52
+
53
+ **Files:**
54
+ - Create: `src/services/tagService.ts`
55
+
56
+ **Step 1: 创建基础结构**
57
+
58
+ ```typescript
59
+ // src/services/tagService.ts
60
+ import { getDatabase } from '../db/schema.js';
61
+ import { EntityRepository } from '../db/entityRepository.js';
62
+
63
+ export interface TagNode {
64
+ name: string;
65
+ level: number;
66
+ memoryCount: number;
67
+ usageCount: number;
68
+ children: TagNode[];
69
+ }
70
+
71
+ export interface TagStats {
72
+ totalTags: number;
73
+ totalMemories: number;
74
+ usageStats: { name: string; count: number }[];
75
+ levelDistribution: Record<number, number>;
76
+ recentlyUsed: { name: string; lastUsed: string }[];
77
+ }
78
+
79
+ export class TagService {
80
+ private db: ReturnType<typeof getDatabase>;
81
+ private entityRepo: EntityRepository;
82
+
83
+ constructor() {
84
+ this.db = getDatabase();
85
+ this.entityRepo = new EntityRepository(this.db);
86
+ }
87
+
88
+ // TODO: 实现方法
89
+ }
90
+ ```
91
+
92
+ **Step 2: 提交**
93
+
94
+ ```bash
95
+ git add src/services/tagService.ts
96
+ git commit -m "feat: add TagService class skeleton"
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Task 4: 实现 getTagTree 方法
102
+
103
+ **Files:**
104
+ - Modify: `src/services/tagService.ts`
105
+
106
+ **Step 1: 实现 getTagTree 方法**
107
+
108
+ ```typescript
109
+ async getTagTree(): Promise<{ totalTags: number; maxLevel: number; tree: TagNode[] }> {
110
+ // 1. 获取所有标签
111
+ const allTags = this.db.prepare(`
112
+ SELECT e.*, COUNT(me.memory_id) as memory_count
113
+ FROM entities e
114
+ LEFT JOIN memory_entities me ON e.id = me.entity_id
115
+ WHERE e.type = 'tag'
116
+ GROUP BY e.id
117
+ `).all() as any[];
118
+
119
+ // 2. 构建映射
120
+ const tagMap = new Map<string, TagNode>();
121
+ for (const tag of allTags) {
122
+ tagMap.set(tag.id, {
123
+ name: tag.name,
124
+ level: tag.level,
125
+ memoryCount: tag.memory_count || 0,
126
+ usageCount: tag.memory_count || 0,
127
+ children: []
128
+ });
129
+ }
130
+
131
+ // 3. 构建树形结构
132
+ const rootTags: TagNode[] = [];
133
+ let maxLevel = 0;
134
+
135
+ for (const tag of allTags) {
136
+ const node = tagMap.get(tag.id)!;
137
+ maxLevel = Math.max(maxLevel, tag.level);
138
+
139
+ if (tag.parent_id && tagMap.has(tag.parent_id)) {
140
+ tagMap.get(tag.parent_id)!.children.push(node);
141
+ } else {
142
+ rootTags.push(node);
143
+ }
144
+ }
145
+
146
+ return { totalTags: allTags.length, maxLevel, tree: rootTags };
147
+ }
148
+ ```
149
+
150
+ **Step 2: 测试运行**
151
+
152
+ ```bash
153
+ npx ts-node -e "
154
+ import { TagService } from './src/services/tagService.js';
155
+ const ts = new TagService();
156
+ ts.getTagTree().then(r => console.log('Tags:', r.totalTags)).catch(e => console.error(e));
157
+ "
158
+ ```
159
+
160
+ Expected: 输出标签数量(可能为 0)
161
+
162
+ **Step 3: 提交**
163
+
164
+ ```bash
165
+ git add src/services/tagService.ts
166
+ git commit -m "feat: implement getTagTree method"
167
+ ```
168
+
169
+ ---
170
+
171
+ ## Task 5: 实现 getTagStats 方法
172
+
173
+ **Files:**
174
+ - Modify: `src/services/tagService.ts`
175
+
176
+ **Step 1: 实现 getTagStats 方法**
177
+
178
+ ```typescript
179
+ async getTagStats(): Promise<TagStats> {
180
+ // 总标签数
181
+ const totalTags = this.db.prepare(`
182
+ SELECT COUNT(*) as count FROM entities WHERE type = 'tag'
183
+ `).get() as { count: number };
184
+
185
+ // 总记忆数
186
+ const totalMemories = this.db.prepare(`
187
+ SELECT COUNT(*) as count FROM memories
188
+ `).get() as { count: number };
189
+
190
+ // 使用频率统计
191
+ const usageStats = this.db.prepare(`
192
+ SELECT e.name, COUNT(me.memory_id) as count
193
+ FROM entities e
194
+ JOIN memory_entities me ON e.id = me.entity_id
195
+ WHERE e.type = 'tag'
196
+ GROUP BY e.id
197
+ ORDER BY count DESC
198
+ LIMIT 20
199
+ `).all() as { name: string; count: number }[];
200
+
201
+ // 层级分布
202
+ const levelDist = this.db.prepare(`
203
+ SELECT level, COUNT(*) as count
204
+ FROM entities
205
+ WHERE type = 'tag'
206
+ GROUP BY level
207
+ `).all() as { level: number; count: number }[];
208
+
209
+ const levelDistribution: Record<number, number> = {};
210
+ for (const d of levelDist) {
211
+ levelDistribution[d.level] = d.count;
212
+ }
213
+
214
+ // 最近使用
215
+ const recentlyUsed = this.db.prepare(`
216
+ SELECT e.name, MAX(me.created_at) as last_used
217
+ FROM entities e
218
+ JOIN memory_entities me ON e.id = me.entity_id
219
+ WHERE e.type = 'tag'
220
+ GROUP BY e.id
221
+ ORDER BY last_used DESC
222
+ LIMIT 10
223
+ `).all() as { name: string; last_used: string }[];
224
+
225
+ return {
226
+ totalTags: totalTags.count,
227
+ totalMemories: totalMemories.count,
228
+ usageStats,
229
+ levelDistribution,
230
+ recentlyUsed
231
+ };
232
+ }
233
+ ```
234
+
235
+ **Step 2: 测试运行**
236
+
237
+ ```bash
238
+ npx ts-node -e "
239
+ import { TagService } from './src/services/tagService.js';
240
+ const ts = new TagService();
241
+ ts.getTagStats().then(r => console.log('Stats:', JSON.stringify(r, null, 2))).catch(e => console.error(e));
242
+ "
243
+ ```
244
+
245
+ Expected: 输出统计数据
246
+
247
+ **Step 3: 提交**
248
+
249
+ ```bash
250
+ git add src/services/tagService.ts
251
+ git commit -m "feat: implement getTagStats method"
252
+ ```
253
+
254
+ ---
255
+
256
+ ## Task 6: 实现 HTML 生成器 - 标签树
257
+
258
+ **Files:**
259
+ - Modify: `src/services/tagService.ts`
260
+
261
+ **Step 1: 添加 generateTreeHtml 方法**
262
+
263
+ ```typescript
264
+ generateTreeHtml(data: { totalTags: number; maxLevel: number; tree: TagNode[] }): string {
265
+ const renderNode = (node: TagNode, indent: number = 0): string => {
266
+ const padding = ' '.repeat(indent);
267
+ let html = `${padding}<div class="tag-item" data-level="${node.level}">\n`;
268
+ html += `${padding} <div class="tag-header" onclick="toggle(this)">\n`;
269
+ html += `${padding} <span class="toggle">${node.children.length ? '▶' : '·'}</span>\n`;
270
+ html += `${padding} <span class="tag-name">${node.name}</span>\n`;
271
+ html += `${padding} <span class="tag-count">(${node.memoryCount}条记忆, ${node.usageCount}次使用)</span>\n`;
272
+ html += `${padding} </div>\n`;
273
+
274
+ if (node.children.length > 0) {
275
+ html += `${padding} <div class="tag-children" style="display:none;">\n`;
276
+ for (const child of node.children) {
277
+ html += renderNode(child, indent + 2);
278
+ }
279
+ html += `${padding} </div>\n`;
280
+ }
281
+
282
+ html += `${padding}</div>\n`;
283
+ return html;
284
+ };
285
+
286
+ let html = `<!DOCTYPE html>
287
+ <html>
288
+ <head>
289
+ <meta charset="UTF-8">
290
+ <title>标签树 - ${data.totalTags} 个标签</title>
291
+ <style>
292
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px; }
293
+ .tag-item { margin: 4px 0; }
294
+ .tag-header { cursor: pointer; padding: 4px 8px; border-radius: 4px; }
295
+ .tag-header:hover { background: #f0f0f0; }
296
+ .toggle { display: inline-block; width: 20px; color: #666; }
297
+ .tag-name { font-weight: 500; color: #333; }
298
+ .tag-count { color: #999; font-size: 12px; margin-left: 8px; }
299
+ .tag-children { margin-left: 20px; border-left: 1px solid #eee; padding-left: 8px; }
300
+ </style>
301
+ </head>
302
+ <body>
303
+ <h1>标签树 (${data.totalTags} 个标签, 最大层级: ${data.maxLevel})</h1>
304
+ <script>
305
+ function toggle(el) {
306
+ const children = el.nextElementSibling;
307
+ if (children) children.style.display = children.style.display === 'none' ? 'block' : 'none';
308
+ const arrow = el.querySelector('.toggle');
309
+ if (arrow) arrow.textContent = children.style.display === 'none' ? '▶' : '▼';
310
+ }
311
+ </script>
312
+ `;
313
+
314
+ for (const node of data.tree) {
315
+ html += renderNode(node);
316
+ }
317
+
318
+ html += `</body></html>`;
319
+ return html;
320
+ }
321
+ ```
322
+
323
+ **Step 2: 提交**
324
+
325
+ ```bash
326
+ git add src/services/tagService.ts
327
+ git commit -m "feat: add generateTreeHtml method"
328
+ ```
329
+
330
+ ---
331
+
332
+ ## Task 7: 实现 HTML 生成器 - 标签统计
333
+
334
+ **Files:**
335
+ - Modify: `src/services/tagService.ts`
336
+
337
+ **Step 1: 添加 generateStatsHtml 方法**
338
+
339
+ ```typescript
340
+ generateStatsHtml(stats: TagStats): string {
341
+ const maxUsage = Math.max(...stats.usageStats.map(s => s.count), 1);
342
+
343
+ let html = `<!DOCTYPE html>
344
+ <html>
345
+ <head>
346
+ <meta charset="UTF-8">
347
+ <title>标签统计</title>
348
+ <style>
349
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px; }
350
+ h1, h2 { color: #333; }
351
+ .stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; }
352
+ .stat-card { background: #f9f9f9; padding: 20px; border-radius: 8px; text-align: center; }
353
+ .stat-number { font-size: 36px; font-weight: bold; color: #4a90d9; }
354
+ .stat-label { color: #666; margin-top: 8px; }
355
+ .bar-chart { margin: 20px 0; }
356
+ .bar { background: #4a90d9; color: white; padding: 8px 12px; margin: 4px 0; border-radius: 4px; }
357
+ .recent-list { list-style: none; padding: 0; }
358
+ .recent-list li { padding: 8px; border-bottom: 1px solid #eee; }
359
+ .level-grid { display: flex; gap: 10px; flex-wrap: wrap; }
360
+ .level-badge { background: #e0e0e0; padding: 8px 16px; border-radius: 16px; }
361
+ </style>
362
+ </head>
363
+ <body>
364
+ <h1>标签统计</h1>
365
+
366
+ <div class="stat-grid">
367
+ <div class="stat-card">
368
+ <div class="stat-number">${stats.totalTags}</div>
369
+ <div class="stat-label">总标签数</div>
370
+ </div>
371
+ <div class="stat-card">
372
+ <div class="stat-number">${stats.totalMemories}</div>
373
+ <div class="stat-label">总记忆数</div>
374
+ </div>
375
+ <div class="stat-card">
376
+ <div class="stat-number">${stats.maxLevel !== undefined ? stats.maxLevel + 1 : '-'}</div>
377
+ <div class="stat-label">层级深度</div>
378
+ </div>
379
+ </div>
380
+
381
+ <h2>使用频率排行</h2>
382
+ <div class="bar-chart">
383
+ `;
384
+
385
+ for (const s of stats.usageStats) {
386
+ const width = Math.round((s.count / maxUsage) * 100);
387
+ html += ` <div class="bar" style="width: ${width}%">${s.name} (${s.count})</div>\n`;
388
+ }
389
+
390
+ html += ` </div>
391
+
392
+ <h2>层级分布</h2>
393
+ <div class="level-grid">
394
+ `;
395
+
396
+ for (const [level, count] of Object.entries(stats.levelDistribution)) {
397
+ html += ` <div class="level-badge">Level ${level}: ${count}</div>\n`;
398
+ }
399
+
400
+ html += ` </div>
401
+
402
+ <h2>最近使用</h2>
403
+ <ul class="recent-list">
404
+ `;
405
+
406
+ for (const r of stats.recentlyUsed) {
407
+ const date = new Date(r.lastUsed).toLocaleDateString('zh-CN');
408
+ html += ` <li>${r.name} - ${date}</li>\n`;
409
+ }
410
+
411
+ html += ` </ul>
412
+ </body>
413
+ </html>`;
414
+
415
+ return html;
416
+ }
417
+ ```
418
+
419
+ **Step 2: 提交**
420
+
421
+ ```bash
422
+ git add src/services/tagService.ts
423
+ git commit -m "feat: add generateStatsHtml method"
424
+ ```
425
+
426
+ ---
427
+
428
+ ## Task 8: 集成到 CLI
429
+
430
+ **Files:**
431
+ - Modify: `src/index.ts`
432
+
433
+ **Step 1: 导入 TagService**
434
+
435
+ 在文件顶部添加:
436
+ ```typescript
437
+ import { TagService } from './services/tagService.js';
438
+ import { writeFile } from 'fs/promises';
439
+ ```
440
+
441
+ **Step 2: 添加 tags 子命令**
442
+
443
+ ```typescript
444
+ // 在现有命令后添加
445
+ .command('tags <action>')
446
+ .description('标签管理命令')
447
+ .option('-o, --output <file>', '输出文件路径')
448
+ .action(async (action, options) => {
449
+ const tagService = new TagService();
450
+ const outputFile = options.output || (action === 'tree' ? 'tags-tree.html' : 'tags-stats.html');
451
+
452
+ if (action === 'tree') {
453
+ const data = await tagService.getTagTree();
454
+ const html = tagService.generateTreeHtml(data);
455
+ await writeFile(outputFile, html);
456
+ console.log(`标签树已生成: ${outputFile}`);
457
+ } else if (action === 'stats') {
458
+ const stats = await tagService.getTagStats();
459
+ const html = tagService.generateStatsHtml(stats);
460
+ await writeFile(outputFile, html);
461
+ console.log(`标签统计已生成: ${outputFile}`);
462
+ } else {
463
+ console.error('未知命令: tree 或 stats');
464
+ process.exit(1);
465
+ }
466
+ });
467
+ ```
468
+
469
+ **Step 3: 测试编译**
470
+
471
+ ```bash
472
+ npm run build
473
+ ```
474
+
475
+ **Step 4: 测试运行**
476
+
477
+ ```bash
478
+ node dist/index.js tags tree -o /tmp/test-tags.html
479
+ cat /tmp/test-tags.html | head -30
480
+ ```
481
+
482
+ Expected: 生成 HTML 文件
483
+
484
+ **Step 5: 提交**
485
+
486
+ ```bash
487
+ git add src/index.ts
488
+ git commit -m "feat: add tags CLI commands"
489
+ ```
490
+
491
+ ---
492
+
493
+ ## Task 9: 最终测试
494
+
495
+ **Step 1: 测试 tree 命令**
496
+
497
+ ```bash
498
+ node dist/index.js tags tree
499
+ ls -la tags-tree.html
500
+ ```
501
+
502
+ Expected: 生成 tags-tree.html
503
+
504
+ **Step 2: 测试 stats 命令**
505
+
506
+ ```bash
507
+ node dist/index.js tags stats
508
+ ls -la tags-stats.html
509
+ ```
510
+
511
+ Expected: 生成 tags-stats.html
512
+
513
+ ---
514
+
515
+ ## Task 10: 合并到主分支
516
+
517
+ **Step 1: 切换到主分支**
518
+
519
+ ```bash
520
+ git checkout main
521
+ ```
522
+
523
+ **Step 2: 合并功能分支**
524
+
525
+ ```bash
526
+ git merge feature/tags-visualization
527
+ ```
528
+
529
+ **Step 3: 推送到远程**
530
+
531
+ ```bash
532
+ git push origin main
533
+ ```
534
+
535
+ **Step 4: 删除功能分支(可选)**
536
+
537
+ ```bash
538
+ git branch -d feature/tags-visualization
539
+ ```
@@ -0,0 +1,11 @@
1
+ {
2
+ "id": "claw-memory",
3
+ "name": "ClawMemory",
4
+ "version": "0.1.0",
5
+ "description": "AI memory system - auto-save conversations and inject memory summary",
6
+ "main": "./dist/index.js",
7
+ "dependencies": {},
8
+ "scripts": {
9
+ "build": "tsc"
10
+ }
11
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@yun-zero/claw-memory",
3
+ "version": "0.1.0",
4
+ "description": "OpenClaw 记忆插件 - 自动保存对话,智能注入上下文",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "claw-memory": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js",
13
+ "dev": "tsx src/index.ts",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "test:e2e": "vitest run test/e2e",
17
+ "test:stress": "vitest run test/stress",
18
+ "test:all": "vitest run"
19
+ },
20
+ "dependencies": {
21
+ "better-sqlite3": "^11.0.0",
22
+ "commander": "^12.0.0",
23
+ "node-cron": "^4.2.1",
24
+ "uuid": "^10.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/better-sqlite3": "^7.6.0",
28
+ "@types/node": "^20.0.0",
29
+ "@types/node-cron": "^3.0.11",
30
+ "@types/uuid": "^10.0.0",
31
+ "tsx": "^4.0.0",
32
+ "typescript": "^5.0.0",
33
+ "vitest": "^2.0.0"
34
+ },
35
+ "keywords": [
36
+ "openclaw",
37
+ "memory",
38
+ "ai",
39
+ "plugin"
40
+ ]
41
+ }