@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,252 @@
1
+ /**
2
+ * Memory Summarizer Service
3
+ * Aggregates memory data for weekly report generation
4
+ */
5
+ import { generateSummaryWithLLM } from '../config/llm.js';
6
+ export class SummarizerService {
7
+ db;
8
+ constructor(db) {
9
+ this.db = db;
10
+ }
11
+ async aggregateMemories(startDate, endDate) {
12
+ // 1. 基础统计 - 查询指定日期范围的 memories
13
+ const basicStats = this.getBasicStats(startDate, endDate);
14
+ // 2. 标签分布 - 查询 memory_entities + entities (type='tag')
15
+ const tagStats = this.getTagStats(startDate, endDate);
16
+ // 3. 关键词 - 查询 memory_entities + entities (type='keyword')
17
+ const keywordStats = this.getKeywordStats(startDate, endDate);
18
+ // 4. 重要性分组 - 按 importance 分组
19
+ const importanceStats = this.getImportanceStats(startDate, endDate);
20
+ // 5. 访问模式 - 按 access_count 和时间排序
21
+ const accessStats = this.getAccessStats(startDate, endDate);
22
+ // 6. 实体关系 - 查询 entity_relations
23
+ const entityStats = this.getEntityStats(startDate, endDate);
24
+ return {
25
+ period: { start: startDate, end: endDate },
26
+ basic: basicStats,
27
+ tags: tagStats,
28
+ topics: keywordStats,
29
+ importance: importanceStats,
30
+ access: accessStats,
31
+ entities: entityStats,
32
+ summary: '' // Will be filled by LLM
33
+ };
34
+ }
35
+ getBasicStats(startDate, endDate) {
36
+ const stmt = this.db.prepare(`
37
+ SELECT
38
+ COUNT(*) as totalMemories,
39
+ COALESCE(SUM(token_count), 0) as totalTokens,
40
+ COALESCE(AVG(importance), 0) as avgImportance
41
+ FROM memories
42
+ WHERE date(created_at) >= date(?)
43
+ AND date(created_at) <= date(?)
44
+ `);
45
+ const result = stmt.get(startDate, endDate);
46
+ return {
47
+ totalMemories: result.totalMemories || 0,
48
+ totalTokens: result.totalTokens || 0,
49
+ avgImportance: parseFloat((result.avgImportance || 0).toFixed(2))
50
+ };
51
+ }
52
+ getTagStats(startDate, endDate) {
53
+ // Get all tags with their counts in the date range
54
+ const stmt = this.db.prepare(`
55
+ SELECT e.name, COUNT(me.memory_id) as count
56
+ FROM memory_entities me
57
+ JOIN entities e ON me.entity_id = e.id
58
+ JOIN memories m ON me.memory_id = m.id
59
+ WHERE e.type = 'tag'
60
+ AND date(m.created_at) >= date(?)
61
+ AND date(m.created_at) <= date(?)
62
+ GROUP BY e.id, e.name
63
+ ORDER BY count DESC
64
+ LIMIT 20
65
+ `);
66
+ const tags = stmt.all(startDate, endDate);
67
+ // Build distribution map
68
+ const tagDistribution = {};
69
+ for (const tag of tags) {
70
+ tagDistribution[tag.name] = tag.count;
71
+ }
72
+ return {
73
+ topTags: tags.slice(0, 10),
74
+ tagDistribution
75
+ };
76
+ }
77
+ getKeywordStats(startDate, endDate) {
78
+ const stmt = this.db.prepare(`
79
+ SELECT e.name, COUNT(me.memory_id) as count
80
+ FROM memory_entities me
81
+ JOIN entities e ON me.entity_id = e.id
82
+ JOIN memories m ON me.memory_id = m.id
83
+ WHERE e.type = 'keyword'
84
+ AND date(m.created_at) >= date(?)
85
+ AND date(m.created_at) <= date(?)
86
+ GROUP BY e.id, e.name
87
+ ORDER BY count DESC
88
+ LIMIT 30
89
+ `);
90
+ const keywordsRaw = stmt.all(startDate, endDate);
91
+ // Map to expected format with 'word' field
92
+ const keywords = keywordsRaw.map(k => ({ word: k.name, count: k.count }));
93
+ // Extract key topics (top 5 most frequent)
94
+ const keyTopics = keywordsRaw.slice(0, 5).map(k => k.name);
95
+ return {
96
+ keywords,
97
+ keyTopics
98
+ };
99
+ }
100
+ getImportanceStats(startDate, endDate) {
101
+ const stmt = this.db.prepare(`
102
+ SELECT id, summary, importance
103
+ FROM memories
104
+ WHERE date(created_at) >= date(?)
105
+ AND date(created_at) <= date(?)
106
+ `);
107
+ const memories = stmt.all(startDate, endDate);
108
+ const highPriority = [];
109
+ const mediumPriority = [];
110
+ const lowPriority = [];
111
+ for (const memory of memories) {
112
+ if (memory.importance > 0.7) {
113
+ highPriority.push(memory);
114
+ }
115
+ else if (memory.importance > 0.3) {
116
+ mediumPriority.push(memory);
117
+ }
118
+ else {
119
+ lowPriority.push(memory);
120
+ }
121
+ }
122
+ return { highPriority, mediumPriority, lowPriority };
123
+ }
124
+ getAccessStats(startDate, endDate) {
125
+ // Most accessed
126
+ const mostAccessedStmt = this.db.prepare(`
127
+ SELECT id, summary, access_count
128
+ FROM memories
129
+ WHERE date(created_at) >= date(?)
130
+ AND date(created_at) <= date(?)
131
+ ORDER BY access_count DESC
132
+ LIMIT 5
133
+ `);
134
+ const mostAccessed = mostAccessedStmt.all(startDate, endDate);
135
+ // Recently created
136
+ const recentlyCreatedStmt = this.db.prepare(`
137
+ SELECT id, summary, created_at
138
+ FROM memories
139
+ WHERE date(created_at) >= date(?)
140
+ AND date(created_at) <= date(?)
141
+ ORDER BY created_at DESC
142
+ LIMIT 5
143
+ `);
144
+ const recentlyCreated = recentlyCreatedStmt.all(startDate, endDate);
145
+ // Recently accessed
146
+ const recentlyAccessedStmt = this.db.prepare(`
147
+ SELECT id, summary, last_accessed_at
148
+ FROM memories
149
+ WHERE date(created_at) >= date(?)
150
+ AND date(created_at) <= date(?)
151
+ AND last_accessed_at IS NOT NULL
152
+ ORDER BY last_accessed_at DESC
153
+ LIMIT 5
154
+ `);
155
+ const recentlyAccessed = recentlyAccessedStmt.all(startDate, endDate);
156
+ return { mostAccessed, recentlyCreated, recentlyAccessed };
157
+ }
158
+ getEntityStats(startDate, endDate) {
159
+ // Get related entities from entity_relations
160
+ const relatedStmt = this.db.prepare(`
161
+ SELECT e1.name as entity, GROUP_CONCAT(e2.name) as related
162
+ FROM entity_relations er
163
+ JOIN entities e1 ON er.source_id = e1.id
164
+ JOIN entities e2 ON er.target_id = e2.id
165
+ WHERE er.relation_type IN ('related', 'similar', 'co_occur')
166
+ GROUP BY e1.id, e1.name
167
+ LIMIT 10
168
+ `);
169
+ const relatedResults = relatedStmt.all();
170
+ const relatedGroups = relatedResults.map(r => ({
171
+ entity: r.entity,
172
+ related: r.related ? r.related.split(',') : []
173
+ }));
174
+ // Get co-occurring tags
175
+ const coOccurStmt = this.db.prepare(`
176
+ SELECT e1.name as tag1, e2.name as tag2, COUNT(*) as count
177
+ FROM memory_entities me1
178
+ JOIN memory_entities me2 ON me1.memory_id = me2.memory_id AND me1.entity_id < me2.entity_id
179
+ JOIN entities e1 ON me1.entity_id = e1.id
180
+ JOIN entities e2 ON me2.entity_id = e2.id
181
+ JOIN memories m ON me1.memory_id = m.id
182
+ WHERE e1.type = 'tag' AND e2.type = 'tag'
183
+ AND date(m.created_at) >= date(?)
184
+ AND date(m.created_at) <= date(?)
185
+ GROUP BY e1.id, e2.id
186
+ ORDER BY count DESC
187
+ LIMIT 10
188
+ `);
189
+ const coOccurring = coOccurStmt.all(startDate, endDate);
190
+ const coOccurringTags = coOccurring.map(c => [c.tag1, c.tag2]);
191
+ return { relatedGroups, coOccurringTags };
192
+ }
193
+ reportToString(report) {
194
+ const lines = [];
195
+ lines.push(`📊 周报期间: ${report.period.start} 至 ${report.period.end}`);
196
+ lines.push('');
197
+ lines.push('## 基础统计');
198
+ lines.push(`- 记忆总数: ${report.basic.totalMemories}`);
199
+ lines.push(`- 总 Token 数: ${report.basic.totalTokens}`);
200
+ lines.push(`- 平均重要性: ${report.basic.avgImportance}`);
201
+ lines.push('');
202
+ if (report.tags.topTags.length > 0) {
203
+ lines.push('## 热门标签');
204
+ for (const tag of report.tags.topTags.slice(0, 5)) {
205
+ lines.push(`- ${tag.name}: ${tag.count} 条`);
206
+ }
207
+ lines.push('');
208
+ }
209
+ if (report.topics.keyTopics.length > 0) {
210
+ lines.push('## 关键主题');
211
+ lines.push(report.topics.keyTopics.join(', '));
212
+ lines.push('');
213
+ }
214
+ if (report.importance.highPriority.length > 0) {
215
+ lines.push('## 高优先级记忆');
216
+ for (const m of report.importance.highPriority.slice(0, 3)) {
217
+ lines.push(`- ${m.summary?.substring(0, 50) || '无摘要'}... (重要性: ${m.importance})`);
218
+ }
219
+ lines.push('');
220
+ }
221
+ return lines.join('\n');
222
+ }
223
+ /**
224
+ * Generate weekly summary using LLM
225
+ */
226
+ async generateWeeklySummary(report) {
227
+ const stats = await this.aggregateMemories(report.startDate, report.endDate);
228
+ const statsString = this.reportToString(stats);
229
+ let prompt = `请为以下周报数据生成一个简洁的中文摘要(100-200字):\n\n${statsString}`;
230
+ if (report.memories && report.memories.length > 0) {
231
+ prompt += `\n\n## 本周重要记忆内容:\n${report.memories.join('\n---\n')}`;
232
+ }
233
+ return await generateSummaryWithLLM(prompt);
234
+ }
235
+ /**
236
+ * Generate monthly summary using LLM
237
+ */
238
+ async generateMonthlySummary(report) {
239
+ const stats = await this.aggregateMemories(report.startDate, report.endDate);
240
+ const statsString = this.reportToString(stats);
241
+ let prompt = `请为以下月报数据生成一个简洁的中文摘要(150-300字):\n\n${statsString}`;
242
+ if (report.memories && report.memories.length > 0) {
243
+ prompt += `\n\n## 本月重要记忆内容:\n${report.memories.join('\n---\n')}`;
244
+ }
245
+ return await generateSummaryWithLLM(prompt);
246
+ }
247
+ }
248
+ /**
249
+ * Alias for SummarizerService for backwards compatibility
250
+ */
251
+ export { SummarizerService as Summarizer };
252
+ //# sourceMappingURL=summarizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summarizer.js","sourceRoot":"","sources":["../../src/services/summarizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import Database from 'better-sqlite3';
2
+ /**
3
+ * TagNode represents a hierarchical tag structure
4
+ */
5
+ export interface TagNode {
6
+ name: string;
7
+ level: number;
8
+ memoryCount: number;
9
+ usageCount: number;
10
+ children: TagNode[];
11
+ }
12
+ /**
13
+ * TagStats provides aggregated statistics about tags
14
+ */
15
+ export interface TagStats {
16
+ totalTags: number;
17
+ totalMemories: number;
18
+ usageStats: {
19
+ name: string;
20
+ count: number;
21
+ }[];
22
+ levelDistribution: Record<number, number>;
23
+ recentlyUsed: {
24
+ name: string;
25
+ lastUsedAt: Date;
26
+ }[];
27
+ }
28
+ /**
29
+ * TagService provides tag management and statistics functionality
30
+ */
31
+ export declare class TagService {
32
+ private db;
33
+ private entityRepo;
34
+ constructor(db: Database.Database);
35
+ /**
36
+ * Get all tags as a hierarchical tree structure with statistics
37
+ */
38
+ getTagTree(): Promise<{
39
+ totalTags: number;
40
+ maxLevel: number;
41
+ tree: TagNode[];
42
+ }>;
43
+ /**
44
+ * Get tag statistics
45
+ */
46
+ getTagStats(): TagStats;
47
+ /**
48
+ * Generate collapsible tag tree HTML
49
+ */
50
+ generateTreeHtml(data: {
51
+ totalTags: number;
52
+ maxLevel: number;
53
+ tree: TagNode[];
54
+ }): string;
55
+ /**
56
+ * Generate tag statistics HTML
57
+ */
58
+ generateStatsHtml(stats: TagStats): string;
59
+ /**
60
+ * Build hierarchical tag tree from flat tag list
61
+ */
62
+ private buildTagTree;
63
+ }
64
+ //# sourceMappingURL=tagService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tagService.d.ts","sourceRoot":"","sources":["../../src/services/tagService.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAGtC;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,IAAI,CAAA;KAAE,EAAE,CAAC;CACpD;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,UAAU,CAAmB;gBAEzB,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAKjC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;IAwCrF;;OAEG;IACH,WAAW,IAAI,QAAQ;IAsEvB;;OAEG;IACH,gBAAgB,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,EAAE,CAAA;KAAE,GAAG,MAAM;IA0DxF;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM;IA0E1C;;OAEG;IACH,OAAO,CAAC,YAAY;CAwCrB"}
@@ -0,0 +1,281 @@
1
+ import { EntityRepository } from '../db/entityRepository.js';
2
+ /**
3
+ * TagService provides tag management and statistics functionality
4
+ */
5
+ export class TagService {
6
+ db;
7
+ entityRepo;
8
+ constructor(db) {
9
+ this.db = db;
10
+ this.entityRepo = new EntityRepository(db);
11
+ }
12
+ /**
13
+ * Get all tags as a hierarchical tree structure with statistics
14
+ */
15
+ async getTagTree() {
16
+ // 1. Get all tags with memory count
17
+ const allTags = this.db.prepare(`
18
+ SELECT e.*, COUNT(me.memory_id) as memory_count
19
+ FROM entities e
20
+ LEFT JOIN memory_entities me ON e.id = me.entity_id
21
+ WHERE e.type = 'tag'
22
+ GROUP BY e.id
23
+ `).all();
24
+ // 2. Build mapping: tagId -> TagNode
25
+ const tagMap = new Map();
26
+ for (const tag of allTags) {
27
+ tagMap.set(tag.id, {
28
+ name: tag.name,
29
+ level: tag.level,
30
+ memoryCount: tag.memory_count || 0,
31
+ usageCount: tag.memory_count || 0,
32
+ children: []
33
+ });
34
+ }
35
+ // 3. Build tree structure via parent_id
36
+ const rootTags = [];
37
+ let maxLevel = 0;
38
+ for (const tag of allTags) {
39
+ const node = tagMap.get(tag.id);
40
+ maxLevel = Math.max(maxLevel, tag.level || 0);
41
+ if (tag.parent_id && tagMap.has(tag.parent_id)) {
42
+ tagMap.get(tag.parent_id).children.push(node);
43
+ }
44
+ else {
45
+ rootTags.push(node);
46
+ }
47
+ }
48
+ return { totalTags: allTags.length, maxLevel, tree: rootTags };
49
+ }
50
+ /**
51
+ * Get tag statistics
52
+ */
53
+ getTagStats() {
54
+ // Get all tag entities
55
+ const tags = this.entityRepo.findByType('tag');
56
+ // Get memory count per tag
57
+ const memoryCountStmt = this.db.prepare(`
58
+ SELECT e.name, COUNT(me.memory_id) as memory_count
59
+ FROM entities e
60
+ LEFT JOIN memory_entities me ON e.id = me.entity_id
61
+ WHERE e.type = 'tag'
62
+ GROUP BY e.id
63
+ `);
64
+ const memoryCounts = memoryCountStmt.all();
65
+ // Get usage count (relevance sum)
66
+ const usageStmt = this.db.prepare(`
67
+ SELECT e.name, SUM(me.relevance) as usage_count
68
+ FROM entities e
69
+ LEFT JOIN memory_entities me ON e.id = me.entity_id
70
+ WHERE e.type = 'tag'
71
+ GROUP BY e.id
72
+ `);
73
+ const usageCounts = usageStmt.all();
74
+ // Calculate level distribution
75
+ const levelDistribution = {};
76
+ for (const tag of tags) {
77
+ levelDistribution[tag.level] = (levelDistribution[tag.level] || 0) + 1;
78
+ }
79
+ // Get total memories with tags
80
+ const totalMemoriesStmt = this.db.prepare(`
81
+ SELECT COUNT(DISTINCT memory_id) as count
82
+ FROM memory_entities me
83
+ JOIN entities e ON me.entity_id = e.id
84
+ WHERE e.type = 'tag'
85
+ `);
86
+ const totalMemoriesResult = totalMemoriesStmt.get();
87
+ // Get recently used tags
88
+ const recentStmt = this.db.prepare(`
89
+ SELECT e.name, MAX(me.created_at) as last_used_at
90
+ FROM entities e
91
+ JOIN memory_entities me ON e.id = me.entity_id
92
+ WHERE e.type = 'tag'
93
+ GROUP BY e.id
94
+ ORDER BY last_used_at DESC
95
+ LIMIT 10
96
+ `);
97
+ const recentlyUsed = recentStmt.all();
98
+ // Build usage stats
99
+ const usageStatsMap = new Map(usageCounts.map(u => [u.name, u.usage_count || 0]));
100
+ const usageStats = tags.map(tag => ({
101
+ name: tag.name,
102
+ count: usageStatsMap.get(tag.name) || 0
103
+ })).sort((a, b) => b.count - a.count);
104
+ return {
105
+ totalTags: tags.length,
106
+ totalMemories: totalMemoriesResult.count,
107
+ usageStats,
108
+ levelDistribution,
109
+ recentlyUsed: recentlyUsed.map(r => ({
110
+ name: r.name,
111
+ lastUsedAt: new Date(r.last_used_at)
112
+ }))
113
+ };
114
+ }
115
+ /**
116
+ * Generate collapsible tag tree HTML
117
+ */
118
+ generateTreeHtml(data) {
119
+ const renderNode = (node, indent = 0) => {
120
+ const padding = ' '.repeat(indent);
121
+ let html = `${padding}<div class="tag-item" data-level="${node.level}">\n`;
122
+ html += `${padding} <div class="tag-header" onclick="toggle(this)">\n`;
123
+ html += `${padding} <span class="toggle">${node.children.length ? '▶' : '·'}</span>\n`;
124
+ html += `${padding} <span class="tag-name">${node.name}</span>\n`;
125
+ html += `${padding} <span class="tag-count">(${node.memoryCount}条记忆, ${node.usageCount}次使用)</span>\n`;
126
+ html += `${padding} </div>\n`;
127
+ if (node.children.length > 0) {
128
+ html += `${padding} <div class="tag-children" style="display:none;">\n`;
129
+ for (const child of node.children) {
130
+ html += renderNode(child, indent + 2);
131
+ }
132
+ html += `${padding} </div>\n`;
133
+ }
134
+ html += `${padding}</div>\n`;
135
+ return html;
136
+ };
137
+ let html = `<!DOCTYPE html>
138
+ <html>
139
+ <head>
140
+ <meta charset="UTF-8">
141
+ <title>标签树 - ${data.totalTags} 个标签</title>
142
+ <style>
143
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px; }
144
+ .tag-item { margin: 4px 0; }
145
+ .tag-header { cursor: pointer; padding: 4px 8px; border-radius: 4px; }
146
+ .tag-header:hover { background: #f0f0f0; }
147
+ .toggle { display: inline-block; width: 20px; color: #666; }
148
+ .tag-name { font-weight: 500; color: #333; }
149
+ .tag-count { color: #999; font-size: 12px; margin-left: 8px; }
150
+ .tag-children { margin-left: 20px; border-left: 1px solid #eee; padding-left: 8px; }
151
+ </style>
152
+ </head>
153
+ <body>
154
+ <h1>标签树 (${data.totalTags} 个标签, 最大层级: ${data.maxLevel})</h1>
155
+ <script>
156
+ function toggle(el) {
157
+ const children = el.nextElementSibling;
158
+ if (children) children.style.display = children.style.display === 'none' ? 'block' : 'none';
159
+ const arrow = el.querySelector('.toggle');
160
+ if (arrow) arrow.textContent = children.style.display === 'none' ? '▶' : '▼';
161
+ }
162
+ </script>
163
+ `;
164
+ for (const node of data.tree) {
165
+ html += renderNode(node);
166
+ }
167
+ html += `</body></html>`;
168
+ return html;
169
+ }
170
+ /**
171
+ * Generate tag statistics HTML
172
+ */
173
+ generateStatsHtml(stats) {
174
+ const maxUsage = Math.max(...stats.usageStats.map(s => s.count), 1);
175
+ let html = `<!DOCTYPE html>
176
+ <html>
177
+ <head>
178
+ <meta charset="UTF-8">
179
+ <title>标签统计</title>
180
+ <style>
181
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px; }
182
+ h1, h2 { color: #333; }
183
+ .stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; }
184
+ .stat-card { background: #f9f9f9; padding: 20px; border-radius: 8px; text-align: center; }
185
+ .stat-number { font-size: 36px; font-weight: bold; color: #4a90d9; }
186
+ .stat-label { color: #666; margin-top: 8px; }
187
+ .bar-chart { margin: 20px 0; }
188
+ .bar { background: #4a90d9; color: white; padding: 8px 12px; margin: 4px 0; border-radius: 4px; }
189
+ .recent-list { list-style: none; padding: 0; }
190
+ .recent-list li { padding: 8px; border-bottom: 1px solid #eee; }
191
+ .level-grid { display: flex; gap: 10px; flex-wrap: wrap; }
192
+ .level-badge { background: #e0e0e0; padding: 8px 16px; border-radius: 16px; }
193
+ </style>
194
+ </head>
195
+ <body>
196
+ <h1>标签统计</h1>
197
+
198
+ <div class="stat-grid">
199
+ <div class="stat-card">
200
+ <div class="stat-number">${stats.totalTags}</div>
201
+ <div class="stat-label">总标签数</div>
202
+ </div>
203
+ <div class="stat-card">
204
+ <div class="stat-number">${stats.totalMemories}</div>
205
+ <div class="stat-label">总记忆数</div>
206
+ </div>
207
+ </div>
208
+
209
+ <h2>使用频率排行</h2>
210
+ <div class="bar-chart">
211
+ `;
212
+ for (const s of stats.usageStats) {
213
+ const width = Math.round((s.count / maxUsage) * 100);
214
+ html += ` <div class="bar" style="width: ${width}%">${s.name} (${s.count})</div>\n`;
215
+ }
216
+ html += ` </div>
217
+
218
+ <h2>层级分布</h2>
219
+ <div class="level-grid">
220
+ `;
221
+ for (const [level, count] of Object.entries(stats.levelDistribution)) {
222
+ html += ` <div class="level-badge">Level ${level}: ${count}</div>\n`;
223
+ }
224
+ html += ` </div>
225
+
226
+ <h2>最近使用</h2>
227
+ <ul class="recent-list">
228
+ `;
229
+ for (const r of stats.recentlyUsed) {
230
+ const date = new Date(r.lastUsedAt).toLocaleDateString('zh-CN');
231
+ html += ` <li>${r.name} - ${date}</li>\n`;
232
+ }
233
+ html += ` </ul>
234
+ </body>
235
+ </html>`;
236
+ return html;
237
+ }
238
+ /**
239
+ * Build hierarchical tag tree from flat tag list
240
+ */
241
+ buildTagTree(tags) {
242
+ const tagMap = new Map();
243
+ const rootNodes = [];
244
+ // First pass: create all nodes
245
+ for (const tag of tags) {
246
+ tagMap.set(tag.name, {
247
+ name: tag.name,
248
+ level: tag.level,
249
+ memoryCount: 0,
250
+ usageCount: 0,
251
+ children: []
252
+ });
253
+ }
254
+ // Second pass: build hierarchy
255
+ for (const tag of tags) {
256
+ const node = tagMap.get(tag.name);
257
+ if (tag.level === 0) {
258
+ rootNodes.push(node);
259
+ }
260
+ else {
261
+ // Find parent based on hierarchical naming (e.g., "a/b/c" has parent "a/b")
262
+ const parts = tag.name.split('/');
263
+ if (parts.length > 1) {
264
+ const parentName = parts.slice(0, -1).join('/');
265
+ const parent = tagMap.get(parentName);
266
+ if (parent) {
267
+ parent.children.push(node);
268
+ }
269
+ else {
270
+ rootNodes.push(node);
271
+ }
272
+ }
273
+ else {
274
+ rootNodes.push(node);
275
+ }
276
+ }
277
+ }
278
+ return rootNodes;
279
+ }
280
+ }
281
+ //# sourceMappingURL=tagService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tagService.js","sourceRoot":"","sources":["../../src/services/tagService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Database } from 'better-sqlite3';
2
+ export declare function registerMemoryTools(tools: any, db: Database, dataDir: string): void;
3
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/tools/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM/C,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,GAAG,EACV,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE,MAAM,QA+HhB"}