stellar-memory 0.5.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 (197) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +362 -0
  3. package/dist/api/routes/analytics.d.ts +15 -0
  4. package/dist/api/routes/analytics.js +131 -0
  5. package/dist/api/routes/analytics.js.map +1 -0
  6. package/dist/api/routes/conflicts.d.ts +12 -0
  7. package/dist/api/routes/conflicts.js +67 -0
  8. package/dist/api/routes/conflicts.js.map +1 -0
  9. package/dist/api/routes/consolidation.d.ts +11 -0
  10. package/dist/api/routes/consolidation.js +63 -0
  11. package/dist/api/routes/consolidation.js.map +1 -0
  12. package/dist/api/routes/constellation.d.ts +4 -0
  13. package/dist/api/routes/constellation.js +84 -0
  14. package/dist/api/routes/constellation.js.map +1 -0
  15. package/dist/api/routes/memories.d.ts +4 -0
  16. package/dist/api/routes/memories.js +219 -0
  17. package/dist/api/routes/memories.js.map +1 -0
  18. package/dist/api/routes/observations.d.ts +10 -0
  19. package/dist/api/routes/observations.js +42 -0
  20. package/dist/api/routes/observations.js.map +1 -0
  21. package/dist/api/routes/orbit.d.ts +4 -0
  22. package/dist/api/routes/orbit.js +71 -0
  23. package/dist/api/routes/orbit.js.map +1 -0
  24. package/dist/api/routes/projects.d.ts +15 -0
  25. package/dist/api/routes/projects.js +121 -0
  26. package/dist/api/routes/projects.js.map +1 -0
  27. package/dist/api/routes/scan.d.ts +4 -0
  28. package/dist/api/routes/scan.js +403 -0
  29. package/dist/api/routes/scan.js.map +1 -0
  30. package/dist/api/routes/sun.d.ts +4 -0
  31. package/dist/api/routes/sun.js +43 -0
  32. package/dist/api/routes/sun.js.map +1 -0
  33. package/dist/api/routes/system.d.ts +4 -0
  34. package/dist/api/routes/system.js +70 -0
  35. package/dist/api/routes/system.js.map +1 -0
  36. package/dist/api/routes/temporal.d.ts +13 -0
  37. package/dist/api/routes/temporal.js +82 -0
  38. package/dist/api/routes/temporal.js.map +1 -0
  39. package/dist/api/server.d.ts +2 -0
  40. package/dist/api/server.js +99 -0
  41. package/dist/api/server.js.map +1 -0
  42. package/dist/api/websocket.d.ts +53 -0
  43. package/dist/api/websocket.js +168 -0
  44. package/dist/api/websocket.js.map +1 -0
  45. package/dist/cli/index.d.ts +12 -0
  46. package/dist/cli/index.js +35 -0
  47. package/dist/cli/index.js.map +1 -0
  48. package/dist/cli/init.d.ts +10 -0
  49. package/dist/cli/init.js +163 -0
  50. package/dist/cli/init.js.map +1 -0
  51. package/dist/engine/analytics.d.ts +93 -0
  52. package/dist/engine/analytics.js +437 -0
  53. package/dist/engine/analytics.js.map +1 -0
  54. package/dist/engine/conflict.d.ts +54 -0
  55. package/dist/engine/conflict.js +322 -0
  56. package/dist/engine/conflict.js.map +1 -0
  57. package/dist/engine/consolidation.d.ts +83 -0
  58. package/dist/engine/consolidation.js +368 -0
  59. package/dist/engine/consolidation.js.map +1 -0
  60. package/dist/engine/constellation.d.ts +66 -0
  61. package/dist/engine/constellation.js +382 -0
  62. package/dist/engine/constellation.js.map +1 -0
  63. package/dist/engine/corona.d.ts +53 -0
  64. package/dist/engine/corona.js +181 -0
  65. package/dist/engine/corona.js.map +1 -0
  66. package/dist/engine/embedding.d.ts +44 -0
  67. package/dist/engine/embedding.js +168 -0
  68. package/dist/engine/embedding.js.map +1 -0
  69. package/dist/engine/gravity.d.ts +63 -0
  70. package/dist/engine/gravity.js +121 -0
  71. package/dist/engine/gravity.js.map +1 -0
  72. package/dist/engine/multiproject.d.ts +75 -0
  73. package/dist/engine/multiproject.js +241 -0
  74. package/dist/engine/multiproject.js.map +1 -0
  75. package/dist/engine/observation.d.ts +82 -0
  76. package/dist/engine/observation.js +357 -0
  77. package/dist/engine/observation.js.map +1 -0
  78. package/dist/engine/orbit.d.ts +91 -0
  79. package/dist/engine/orbit.js +249 -0
  80. package/dist/engine/orbit.js.map +1 -0
  81. package/dist/engine/planet.d.ts +64 -0
  82. package/dist/engine/planet.js +432 -0
  83. package/dist/engine/planet.js.map +1 -0
  84. package/dist/engine/procedural.d.ts +71 -0
  85. package/dist/engine/procedural.js +259 -0
  86. package/dist/engine/procedural.js.map +1 -0
  87. package/dist/engine/quality.d.ts +48 -0
  88. package/dist/engine/quality.js +245 -0
  89. package/dist/engine/quality.js.map +1 -0
  90. package/dist/engine/repository.d.ts +79 -0
  91. package/dist/engine/repository.js +13 -0
  92. package/dist/engine/repository.js.map +1 -0
  93. package/dist/engine/sun.d.ts +61 -0
  94. package/dist/engine/sun.js +240 -0
  95. package/dist/engine/sun.js.map +1 -0
  96. package/dist/engine/temporal.d.ts +67 -0
  97. package/dist/engine/temporal.js +283 -0
  98. package/dist/engine/temporal.js.map +1 -0
  99. package/dist/engine/types.d.ts +179 -0
  100. package/dist/engine/types.js +27 -0
  101. package/dist/engine/types.js.map +1 -0
  102. package/dist/index.d.ts +2 -0
  103. package/dist/index.js +60 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/mcp/connector-registry.d.ts +20 -0
  106. package/dist/mcp/connector-registry.js +35 -0
  107. package/dist/mcp/connector-registry.js.map +1 -0
  108. package/dist/mcp/server.d.ts +13 -0
  109. package/dist/mcp/server.js +242 -0
  110. package/dist/mcp/server.js.map +1 -0
  111. package/dist/mcp/tools/daemon-tool.d.ts +16 -0
  112. package/dist/mcp/tools/daemon-tool.js +58 -0
  113. package/dist/mcp/tools/daemon-tool.js.map +1 -0
  114. package/dist/mcp/tools/ingestion-tools.d.ts +20 -0
  115. package/dist/mcp/tools/ingestion-tools.js +34 -0
  116. package/dist/mcp/tools/ingestion-tools.js.map +1 -0
  117. package/dist/mcp/tools/memory-tools.d.ts +122 -0
  118. package/dist/mcp/tools/memory-tools.js +1037 -0
  119. package/dist/mcp/tools/memory-tools.js.map +1 -0
  120. package/dist/scanner/cloud/github.d.ts +34 -0
  121. package/dist/scanner/cloud/github.js +260 -0
  122. package/dist/scanner/cloud/github.js.map +1 -0
  123. package/dist/scanner/cloud/google-drive.d.ts +30 -0
  124. package/dist/scanner/cloud/google-drive.js +289 -0
  125. package/dist/scanner/cloud/google-drive.js.map +1 -0
  126. package/dist/scanner/cloud/notion.d.ts +33 -0
  127. package/dist/scanner/cloud/notion.js +231 -0
  128. package/dist/scanner/cloud/notion.js.map +1 -0
  129. package/dist/scanner/cloud/slack.d.ts +38 -0
  130. package/dist/scanner/cloud/slack.js +282 -0
  131. package/dist/scanner/cloud/slack.js.map +1 -0
  132. package/dist/scanner/cloud/types.d.ts +73 -0
  133. package/dist/scanner/cloud/types.js +9 -0
  134. package/dist/scanner/cloud/types.js.map +1 -0
  135. package/dist/scanner/index.d.ts +35 -0
  136. package/dist/scanner/index.js +420 -0
  137. package/dist/scanner/index.js.map +1 -0
  138. package/dist/scanner/local/filesystem.d.ts +33 -0
  139. package/dist/scanner/local/filesystem.js +203 -0
  140. package/dist/scanner/local/filesystem.js.map +1 -0
  141. package/dist/scanner/local/git.d.ts +24 -0
  142. package/dist/scanner/local/git.js +161 -0
  143. package/dist/scanner/local/git.js.map +1 -0
  144. package/dist/scanner/local/parsers/code.d.ts +3 -0
  145. package/dist/scanner/local/parsers/code.js +127 -0
  146. package/dist/scanner/local/parsers/code.js.map +1 -0
  147. package/dist/scanner/local/parsers/index.d.ts +11 -0
  148. package/dist/scanner/local/parsers/index.js +24 -0
  149. package/dist/scanner/local/parsers/index.js.map +1 -0
  150. package/dist/scanner/local/parsers/json-parser.d.ts +3 -0
  151. package/dist/scanner/local/parsers/json-parser.js +117 -0
  152. package/dist/scanner/local/parsers/json-parser.js.map +1 -0
  153. package/dist/scanner/local/parsers/markdown.d.ts +3 -0
  154. package/dist/scanner/local/parsers/markdown.js +120 -0
  155. package/dist/scanner/local/parsers/markdown.js.map +1 -0
  156. package/dist/scanner/local/parsers/text.d.ts +3 -0
  157. package/dist/scanner/local/parsers/text.js +41 -0
  158. package/dist/scanner/local/parsers/text.js.map +1 -0
  159. package/dist/scanner/metadata-scanner.d.ts +67 -0
  160. package/dist/scanner/metadata-scanner.js +356 -0
  161. package/dist/scanner/metadata-scanner.js.map +1 -0
  162. package/dist/scanner/types.d.ts +47 -0
  163. package/dist/scanner/types.js +19 -0
  164. package/dist/scanner/types.js.map +1 -0
  165. package/dist/service/daemon.d.ts +23 -0
  166. package/dist/service/daemon.js +105 -0
  167. package/dist/service/daemon.js.map +1 -0
  168. package/dist/service/scheduler.d.ts +73 -0
  169. package/dist/service/scheduler.js +281 -0
  170. package/dist/service/scheduler.js.map +1 -0
  171. package/dist/storage/database.d.ts +10 -0
  172. package/dist/storage/database.js +265 -0
  173. package/dist/storage/database.js.map +1 -0
  174. package/dist/storage/queries.d.ts +85 -0
  175. package/dist/storage/queries.js +865 -0
  176. package/dist/storage/queries.js.map +1 -0
  177. package/dist/storage/sqlite-repository.d.ts +32 -0
  178. package/dist/storage/sqlite-repository.js +68 -0
  179. package/dist/storage/sqlite-repository.js.map +1 -0
  180. package/dist/storage/vec.d.ts +62 -0
  181. package/dist/storage/vec.js +111 -0
  182. package/dist/storage/vec.js.map +1 -0
  183. package/dist/utils/config.d.ts +5 -0
  184. package/dist/utils/config.js +60 -0
  185. package/dist/utils/config.js.map +1 -0
  186. package/dist/utils/logger.d.ts +36 -0
  187. package/dist/utils/logger.js +86 -0
  188. package/dist/utils/logger.js.map +1 -0
  189. package/dist/utils/time.d.ts +21 -0
  190. package/dist/utils/time.js +42 -0
  191. package/dist/utils/time.js.map +1 -0
  192. package/dist/utils/tokenizer.d.ts +13 -0
  193. package/dist/utils/tokenizer.js +46 -0
  194. package/dist/utils/tokenizer.js.map +1 -0
  195. package/package.json +77 -0
  196. package/scripts/check-node.mjs +36 -0
  197. package/scripts/setup.mjs +157 -0
@@ -0,0 +1,259 @@
1
+ /**
2
+ * procedural.ts — Procedural Memory ("Navigation Rules")
3
+ *
4
+ * Procedural memories are behavioral rules learned from patterns in other
5
+ * memories. They represent "how we do things here" — conventions, workflows,
6
+ * and recurring solutions extracted from observed repetition.
7
+ *
8
+ * Key behaviors:
9
+ * - Pattern detection: group memories by tags, find groups with 3+ members
10
+ * - Rule creation: procedural memories start with high impact (0.9)
11
+ * - Slow decay: procedural memories decay at 30% of the normal rate
12
+ * - Sun integration: top 5 rules appear in a dedicated section
13
+ */
14
+ import { randomUUID, createHash } from 'node:crypto';
15
+ import { getMemoriesByProject, insertMemory } from '../storage/queries.js';
16
+ import { getConfig } from '../utils/config.js';
17
+ import { recencyScore, frequencyScore, importanceToDistance, } from './orbit.js';
18
+ // ---------------------------------------------------------------------------
19
+ // Pattern detection
20
+ // ---------------------------------------------------------------------------
21
+ /**
22
+ * Group memories by individual tag. Only groups with 3+ memories are kept —
23
+ * anything below that threshold is not a stable enough pattern to act on.
24
+ */
25
+ function findRepeatedPatterns(memories) {
26
+ const tagGroups = new Map();
27
+ for (const memory of memories) {
28
+ const tags = Array.isArray(memory.tags) ? memory.tags : [];
29
+ for (const tag of tags) {
30
+ if (!tagGroups.has(tag))
31
+ tagGroups.set(tag, []);
32
+ tagGroups.get(tag).push(memory);
33
+ }
34
+ }
35
+ // Only keep groups with 3+ memories
36
+ return new Map([...tagGroups].filter(([, mems]) => mems.length >= 3));
37
+ }
38
+ /**
39
+ * Extract the most representative keywords from a group of memories.
40
+ * Uses the most common non-trivial words across all content.
41
+ */
42
+ function extractGroupKeywords(memories) {
43
+ const stopWords = new Set([
44
+ 'the', 'a', 'an', 'is', 'it', 'in', 'on', 'at', 'to', 'for',
45
+ 'of', 'and', 'or', 'but', 'with', 'from', 'that', 'this', 'was',
46
+ 'are', 'be', 'been', 'been', 'by', 'as', 'not', 'use', 'used',
47
+ ]);
48
+ const wordCount = new Map();
49
+ for (const mem of memories) {
50
+ const words = (mem.content + ' ' + mem.summary)
51
+ .toLowerCase()
52
+ .split(/[\s,.\-:;()\[\]{}'"!?/\\]+/)
53
+ .filter(w => w.length > 3 && !stopWords.has(w));
54
+ for (const w of words) {
55
+ wordCount.set(w, (wordCount.get(w) ?? 0) + 1);
56
+ }
57
+ }
58
+ return [...wordCount.entries()]
59
+ .sort((a, b) => b[1] - a[1])
60
+ .slice(0, 5)
61
+ .map(([w]) => w);
62
+ }
63
+ /**
64
+ * Determine if a tag group shares a consistent memory type (majority rules).
65
+ */
66
+ function dominantType(memories) {
67
+ const typeCounts = new Map();
68
+ for (const m of memories) {
69
+ typeCounts.set(m.type, (typeCounts.get(m.type) ?? 0) + 1);
70
+ }
71
+ const dominant = [...typeCounts.entries()].sort((a, b) => b[1] - a[1])[0];
72
+ if (!dominant)
73
+ return null;
74
+ // Return the dominant type only if it represents at least 60% of the group
75
+ return dominant[1] / memories.length >= 0.6 ? dominant[0] : null;
76
+ }
77
+ /**
78
+ * Detect patterns in the memory corpus that are strong enough to warrant
79
+ * a procedural rule.
80
+ *
81
+ * Returns one candidate per tag-group that has 3+ members, describing the
82
+ * observed pattern and suggesting a concrete rule.
83
+ */
84
+ export function detectProceduralPattern(memories, project) {
85
+ const groups = findRepeatedPatterns(memories);
86
+ const results = [];
87
+ for (const [tag, groupMemories] of groups) {
88
+ const keywords = extractGroupKeywords(groupMemories);
89
+ const type = dominantType(groupMemories);
90
+ const frequency = groupMemories.length;
91
+ let pattern;
92
+ let suggestedRule;
93
+ if (type === 'error') {
94
+ pattern = `Repeated error pattern tagged "${tag}" (${frequency} occurrences)`;
95
+ suggestedRule = keywords.length > 0
96
+ ? `When encountering ${tag}-related issues: check ${keywords.slice(0, 3).join(', ')}`
97
+ : `When encountering ${tag}-related issues: review established fix pattern`;
98
+ }
99
+ else if (type === 'decision') {
100
+ pattern = `Recurring decision pattern tagged "${tag}" (${frequency} occurrences)`;
101
+ suggestedRule = keywords.length > 0
102
+ ? `For ${tag} decisions: consistently apply ${keywords.slice(0, 3).join(', ')}`
103
+ : `For ${tag} decisions: follow established decision criteria`;
104
+ }
105
+ else {
106
+ pattern = `Repeated context tagged "${tag}" across ${frequency} memories`;
107
+ suggestedRule = keywords.length > 0
108
+ ? `In ${project}: ${tag} work consistently involves ${keywords.slice(0, 3).join(', ')}`
109
+ : `In ${project}: ${tag} is a key recurring theme`;
110
+ }
111
+ results.push({ pattern, frequency, suggestedRule });
112
+ }
113
+ // Sort by frequency descending — strongest patterns first
114
+ return results.sort((a, b) => b.frequency - a.frequency);
115
+ }
116
+ // ---------------------------------------------------------------------------
117
+ // Procedural memory creation
118
+ // ---------------------------------------------------------------------------
119
+ /**
120
+ * Create a procedural memory from a learned rule and its supporting evidence.
121
+ *
122
+ * Procedural memories:
123
+ * - Use type 'procedural'
124
+ * - Start with high impact (0.9) so they orbit close to the sun
125
+ * - Include both the rule and the evidence that generated it
126
+ * - Are tagged with 'procedural' plus terms extracted from the rule
127
+ */
128
+ export function createProceduralMemory(rule, evidence, project) {
129
+ const config = getConfig();
130
+ const impact = 0.9;
131
+ const content = `Rule: ${rule}\nEvidence: ${evidence.join(', ')}`;
132
+ const summary = rule.length > 80 ? rule.slice(0, 80).trimEnd() + '…' : rule;
133
+ // Extract meaningful terms from the rule text for tags
134
+ const ruleWords = rule
135
+ .toLowerCase()
136
+ .split(/[\s,.:;()\-]+/)
137
+ .filter(w => w.length > 3);
138
+ const tags = ['procedural', ...new Set(ruleWords.slice(0, 4))];
139
+ // Compute initial importance (new procedural starts strong)
140
+ const now = new Date().toISOString();
141
+ const rec = recencyScore(null, now, config.decayHalfLifeHours);
142
+ const freq = frequencyScore(0, config.frequencySaturationPoint);
143
+ const rel = 0.5; // reasonable default for new procedural memory
144
+ const total = Math.min(1.0, config.weights.recency * rec +
145
+ config.weights.frequency * freq +
146
+ config.weights.impact * impact +
147
+ config.weights.relevance * rel);
148
+ const distance = importanceToDistance(total);
149
+ const contentHash = createHash('sha256').update(content).digest('hex');
150
+ return insertMemory({
151
+ id: randomUUID(),
152
+ project,
153
+ content,
154
+ summary,
155
+ type: 'procedural',
156
+ tags,
157
+ distance,
158
+ importance: total,
159
+ velocity: 0,
160
+ impact,
161
+ access_count: 0,
162
+ last_accessed_at: null,
163
+ metadata: { evidence_count: evidence.length },
164
+ content_hash: contentHash,
165
+ created_at: now,
166
+ updated_at: now,
167
+ deleted_at: null,
168
+ });
169
+ }
170
+ // ---------------------------------------------------------------------------
171
+ // Retrieval
172
+ // ---------------------------------------------------------------------------
173
+ /**
174
+ * Get all procedural memories for a project, sorted by importance descending.
175
+ * These feed into the sun content formatter and the suggest logic.
176
+ */
177
+ export function getProceduralMemories(project) {
178
+ const all = getMemoriesByProject(project);
179
+ return all
180
+ .filter(m => m.type === 'procedural')
181
+ .sort((a, b) => b.importance - a.importance);
182
+ }
183
+ // ---------------------------------------------------------------------------
184
+ // Sun content formatting
185
+ // ---------------------------------------------------------------------------
186
+ /**
187
+ * Format procedural memories as a concise "Navigation Rules" section
188
+ * suitable for inclusion in the sun resource content.
189
+ *
190
+ * At most 5 rules are shown (most important first). Each rule is rendered
191
+ * as a single numbered line extracted from the content (the "Rule: ..." part).
192
+ */
193
+ export function formatProceduralSection(memories) {
194
+ if (memories.length === 0)
195
+ return '';
196
+ const top = memories.slice(0, 5);
197
+ const lines = top.map((m, i) => {
198
+ // Extract just the rule line from "Rule: ...\nEvidence: ..."
199
+ const match = m.content.match(/^Rule:\s*(.+?)(\n|$)/);
200
+ const rule = match ? match[1].trim() : m.summary;
201
+ return ` ${i + 1}. ${rule}`;
202
+ });
203
+ return `Navigation Rules:\n${lines.join('\n')}`;
204
+ }
205
+ // ---------------------------------------------------------------------------
206
+ // Decay multiplier
207
+ // ---------------------------------------------------------------------------
208
+ /**
209
+ * Procedural memories are hard-won knowledge and should be highly durable.
210
+ * Return 0.3 so that the effective half-life is ~3.3x longer than normal.
211
+ *
212
+ * Used by orbit.ts when calculating recency score for procedural memories:
213
+ * effectiveHalfLife = baseHalfLife / getProceduralDecayMultiplier()
214
+ */
215
+ export function getProceduralDecayMultiplier() {
216
+ return 0.3;
217
+ }
218
+ // ---------------------------------------------------------------------------
219
+ // Rule suggestion
220
+ // ---------------------------------------------------------------------------
221
+ /**
222
+ * Analyse the last 50 memories and suggest concrete procedural rules.
223
+ *
224
+ * Returns candidates sorted by confidence (highest first). Confidence is
225
+ * the proportion of the tag-group that supports a consistent pattern.
226
+ *
227
+ * Skips rules that already exist as procedural memories (by rule text match).
228
+ */
229
+ export function suggestRules(recentMemories) {
230
+ const sample = recentMemories.slice(0, 50);
231
+ const groups = findRepeatedPatterns(sample);
232
+ const results = [];
233
+ for (const [tag, groupMemories] of groups) {
234
+ const total = groupMemories.length;
235
+ const type = dominantType(groupMemories);
236
+ const keywords = extractGroupKeywords(groupMemories);
237
+ // Confidence: how consistently the group points in one direction
238
+ const confidence = Math.min(1.0, (total - 2) / 10 + (type ? 0.2 : 0));
239
+ let rule;
240
+ if (type === 'error') {
241
+ rule = keywords.length > 0
242
+ ? `When facing ${tag} errors: address ${keywords.slice(0, 2).join(' and ')}`
243
+ : `Document and track ${tag} error resolutions systematically`;
244
+ }
245
+ else if (type === 'decision') {
246
+ rule = keywords.length > 0
247
+ ? `For ${tag} decisions: apply ${keywords.slice(0, 2).join(' and ')} criteria`
248
+ : `Maintain consistent decision criteria for ${tag}`;
249
+ }
250
+ else {
251
+ rule = keywords.length > 0
252
+ ? `${tag} work follows pattern: ${keywords.slice(0, 3).join(', ')}`
253
+ : `Track ${tag} as a key recurring theme`;
254
+ }
255
+ results.push({ rule, confidence, evidence: groupMemories });
256
+ }
257
+ return results.sort((a, b) => b.confidence - a.confidence);
258
+ }
259
+ //# sourceMappingURL=procedural.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"procedural.js","sourceRoot":"","sources":["../../src/engine/procedural.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EACL,YAAY,EACZ,cAAc,EACd,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;GAGG;AACH,SAAS,oBAAoB,CAAC,QAAkB;IAC9C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE9C,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAChD,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,oCAAoC;IACpC,OAAO,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,QAAkB;IAC9C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;QACxB,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK;QAC3D,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK;QAC/D,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM;KAC9D,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC;aAC5C,WAAW,EAAE;aACb,KAAK,CAAC,4BAA4B,CAAC;aACnC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAElD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;SAC5B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3B,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,QAAkB;IACtC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,QAAQ,GAAG,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,OAAO,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAAkB,EAClB,OAAe;IAEf,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAyE,EAAE,CAAC;IAEzF,KAAK,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,MAAM,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC;QAEvC,IAAI,OAAe,CAAC;QACpB,IAAI,aAAqB,CAAC;QAE1B,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,OAAO,GAAG,kCAAkC,GAAG,MAAM,SAAS,eAAe,CAAC;YAC9E,aAAa,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACjC,CAAC,CAAC,qBAAqB,GAAG,0BAA0B,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACrF,CAAC,CAAC,qBAAqB,GAAG,iDAAiD,CAAC;QAChF,CAAC;aAAM,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAO,GAAG,sCAAsC,GAAG,MAAM,SAAS,eAAe,CAAC;YAClF,aAAa,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACjC,CAAC,CAAC,OAAO,GAAG,kCAAkC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBAC/E,CAAC,CAAC,OAAO,GAAG,kDAAkD,CAAC;QACnE,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,4BAA4B,GAAG,YAAY,SAAS,WAAW,CAAC;YAC1E,aAAa,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACjC,CAAC,CAAC,MAAM,OAAO,KAAK,GAAG,+BAA+B,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACvF,CAAC,CAAC,MAAM,OAAO,KAAK,GAAG,2BAA2B,CAAC;QACvD,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,0DAA0D;IAC1D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;AAC3D,CAAC;AAED,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,QAAkB,EAClB,OAAe;IAEf,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,GAAG,CAAC;IAEnB,MAAM,OAAO,GAAG,SAAS,IAAI,eAAe,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAClE,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAE5E,uDAAuD;IACvD,MAAM,SAAS,GAAG,IAAI;SACnB,WAAW,EAAE;SACb,KAAK,CAAC,eAAe,CAAC;SACtB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,CAAC,YAAY,EAAE,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/D,4DAA4D;IAC5D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAG,cAAc,CAAC,CAAC,EAAE,MAAM,CAAC,wBAAwB,CAAC,CAAC;IAChE,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,+CAA+C;IAEhE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,GAAG,EACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAG,GAAG;QAC1B,MAAM,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI;QAC/B,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM;QAC9B,MAAM,CAAC,OAAO,CAAC,SAAS,GAAG,GAAG,CACjC,CAAC;IAEF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEvE,OAAO,YAAY,CAAC;QAClB,EAAE,EAAE,UAAU,EAAE;QAChB,OAAO;QACP,OAAO;QACP,OAAO;QACP,IAAI,EAAE,YAA0B;QAChC,IAAI;QACJ,QAAQ;QACR,UAAU,EAAE,KAAK;QACjB,QAAQ,EAAE,CAAC;QACX,MAAM;QACN,YAAY,EAAE,CAAC;QACf,gBAAgB,EAAE,IAAI;QACtB,QAAQ,EAAE,EAAE,cAAc,EAAE,QAAQ,CAAC,MAAM,EAAE;QAC7C,YAAY,EAAE,WAAW;QACzB,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACnD,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,GAAG;SACP,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC;SACpC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;AACjD,CAAC;AAED,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAkB;IACxD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEjC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC7B,6DAA6D;QAC7D,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACjD,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,OAAO,sBAAsB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B;IAC1C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,cAAwB;IAExB,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAoE,EAAE,CAAC;IAEpF,KAAK,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,MAAM,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC;QACnC,MAAM,IAAI,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;QAErD,iEAAiE;QACjE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEtE,IAAI,IAAY,CAAC;QACjB,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACxB,CAAC,CAAC,eAAe,GAAG,oBAAoB,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;gBAC5E,CAAC,CAAC,sBAAsB,GAAG,mCAAmC,CAAC;QACnE,CAAC;aAAM,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACxB,CAAC,CAAC,OAAO,GAAG,qBAAqB,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW;gBAC9E,CAAC,CAAC,6CAA6C,GAAG,EAAE,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACxB,CAAC,CAAC,GAAG,GAAG,0BAA0B,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACnE,CAAC,CAAC,SAAS,GAAG,2BAA2B,CAAC;QAC9C,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * quality.ts — Memory Quality Scoring ("Planet Grading")
3
+ *
4
+ * Grades each memory on four dimensions:
5
+ *
6
+ * - SPECIFICITY : How much concrete detail does the content contain?
7
+ * - ACTIONABILITY : Can someone act on this? Does it say *how*?
8
+ * - UNIQUENESS : Is this information available in other memories?
9
+ * - FRESHNESS : How recently was this memory verified / accessed?
10
+ *
11
+ * The overall score is a weighted average. Low-quality memories are pushed
12
+ * farther from the sun via qualityOrbitAdjustment().
13
+ *
14
+ * All scoring is purely algorithmic — no LLM calls.
15
+ */
16
+ import type { Memory, QualityScore } from './types.js';
17
+ /**
18
+ * Calculate the quality score for a single memory.
19
+ *
20
+ * If allMemories is provided, uniqueness is computed against those peers.
21
+ * Without it, uniqueness defaults to 0.5 (neutral).
22
+ */
23
+ export declare function calculateQuality(memory: Memory, allMemories?: Memory[]): QualityScore;
24
+ /**
25
+ * Score every non-deleted memory in a project and persist the results.
26
+ * Returns aggregate stats.
27
+ */
28
+ export declare function scoreAllMemories(project: string): {
29
+ scored: number;
30
+ avgQuality: number;
31
+ };
32
+ /**
33
+ * Generate feedback tips for a newly stored memory.
34
+ *
35
+ * Returns null when the memory is already high quality (overall >= 0.7).
36
+ * Otherwise returns up to 2 actionable tips (in Korean, matching project locale).
37
+ */
38
+ export declare function getQualityFeedback(quality: QualityScore): string | null;
39
+ /**
40
+ * Return a distance multiplier based on quality.
41
+ *
42
+ * Low-quality memories drift outward to make room for high-quality ones.
43
+ * - quality >= 0.7 : 1.0 (no adjustment)
44
+ * - quality 0.4–0.7: 1.2 (slightly further)
45
+ * - quality < 0.4 : 1.5 (noticeably further)
46
+ */
47
+ export declare function qualityOrbitAdjustment(quality: number): number;
48
+ //# sourceMappingURL=quality.d.ts.map
@@ -0,0 +1,245 @@
1
+ /**
2
+ * quality.ts — Memory Quality Scoring ("Planet Grading")
3
+ *
4
+ * Grades each memory on four dimensions:
5
+ *
6
+ * - SPECIFICITY : How much concrete detail does the content contain?
7
+ * - ACTIONABILITY : Can someone act on this? Does it say *how*?
8
+ * - UNIQUENESS : Is this information available in other memories?
9
+ * - FRESHNESS : How recently was this memory verified / accessed?
10
+ *
11
+ * The overall score is a weighted average. Low-quality memories are pushed
12
+ * farther from the sun via qualityOrbitAdjustment().
13
+ *
14
+ * All scoring is purely algorithmic — no LLM calls.
15
+ */
16
+ import { getMemoriesByProject, updateQualityScore } from '../storage/queries.js';
17
+ // ---------------------------------------------------------------------------
18
+ // Named-entity extraction
19
+ // ---------------------------------------------------------------------------
20
+ /**
21
+ * Extract named entities and technical identifiers from a block of text.
22
+ *
23
+ * Looks for:
24
+ * - PascalCase / camelCase identifiers
25
+ * - kebab-case and snake_case identifiers (length > 4)
26
+ * - Semver / version numbers (v1.2.3, ^2.0, ~3)
27
+ * - File paths (/foo/bar, ./src/index.ts, C:\...)
28
+ * - URLs (http://, https://)
29
+ */
30
+ function extractNamedEntities(text) {
31
+ const found = [];
32
+ // PascalCase / camelCase
33
+ const camelPascal = text.match(/\b[A-Z][a-zA-Z0-9]{2,}\b|\b[a-z][a-z0-9]*[A-Z][a-zA-Z0-9]*\b/g);
34
+ if (camelPascal)
35
+ found.push(...camelPascal);
36
+ // kebab-case and snake_case (require at least one separator and 2+ segments)
37
+ const kebabSnake = text.match(/\b[a-z][a-z0-9]*(?:[-_][a-z0-9]+){1,}\b/g);
38
+ if (kebabSnake)
39
+ found.push(...kebabSnake.filter(t => t.length > 4));
40
+ // Version numbers
41
+ const versions = text.match(/\bv?\d+\.\d+(?:\.\d+)?(?:-[a-z0-9.]+)?\b|\^?\d+\.\d+|\~\d+\.\d+/gi);
42
+ if (versions)
43
+ found.push(...versions);
44
+ // File paths (Unix, Windows, relative)
45
+ const paths = text.match(/(?:\/[\w.\-]+){2,}|(?:\.\/)[\w.\-/]+|[A-Za-z]:\\[\w.\\\-]+/g);
46
+ if (paths)
47
+ found.push(...paths);
48
+ // URLs
49
+ const urls = text.match(/https?:\/\/[^\s)>"']+/g);
50
+ if (urls)
51
+ found.push(...urls);
52
+ return [...new Set(found)];
53
+ }
54
+ // ---------------------------------------------------------------------------
55
+ // Specificity
56
+ // ---------------------------------------------------------------------------
57
+ function scoreSpecificity(memory) {
58
+ const text = memory.content + ' ' + memory.summary;
59
+ const namedEntities = extractNamedEntities(text);
60
+ const hasNumbers = /\b\d+(?:\.\d+)?\b/.test(text);
61
+ const hasPaths = /(?:\/[\w.\-]+){2,}|(?:\.\/)[\w.\-/]+|[A-Za-z]:\\[\w.\\\-]+/.test(text);
62
+ const contentLength = memory.content.length;
63
+ return Math.min(1.0, namedEntities.length * 0.1 +
64
+ (hasNumbers ? 0.2 : 0) +
65
+ (hasPaths ? 0.2 : 0) +
66
+ Math.min(0.3, contentLength / 500));
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // Actionability
70
+ // ---------------------------------------------------------------------------
71
+ const ACTION_VERBS_EN = [
72
+ 'use', 'run', 'install', 'configure', 'fix', 'add', 'remove', 'update',
73
+ 'set', 'enable', 'disable', 'check', 'ensure', 'avoid', 'always', 'never',
74
+ 'prefer', 'call', 'import', 'export', 'create', 'delete', 'replace',
75
+ ];
76
+ const ACTION_VERBS_KO = [
77
+ '사용', '실행', '설치', '수정', '추가', '삭제', '확인', '설정', '적용',
78
+ '피해야', '항상', '절대', '선호', '호출', '가져오기', '내보내기',
79
+ ];
80
+ const INSTRUCTION_PATTERNS = [
81
+ /\bstep \d+\b/i,
82
+ /\b\d+\.\s+[A-Z]/, // numbered list "1. Do something"
83
+ /\bfirst\b.{0,30}\bthen\b/i,
84
+ /\bmake sure\b/i,
85
+ /\bdon['']t\b/i,
86
+ /\b하면\s/, // Korean conditional
87
+ /\b다음\s+단계\b/, // Korean "next step"
88
+ ];
89
+ const CONDITIONAL_PATTERNS = [
90
+ /\bif\b.{1,60}\bthen\b/i,
91
+ /\bwhen\b.{1,60},\s/i,
92
+ /\bunless\b/i,
93
+ /\botherwise\b/i,
94
+ /\b이면\b/, // Korean conditional
95
+ /\b경우에\b/,
96
+ ];
97
+ function scoreActionability(memory) {
98
+ const text = memory.content.toLowerCase();
99
+ const hasActionVerbEn = ACTION_VERBS_EN.some(v => text.includes(v));
100
+ const hasActionVerbKo = ACTION_VERBS_KO.some(v => memory.content.includes(v));
101
+ const hasActionVerb = hasActionVerbEn || hasActionVerbKo;
102
+ const hasInstruction = INSTRUCTION_PATTERNS.some(p => p.test(memory.content));
103
+ const hasConditional = CONDITIONAL_PATTERNS.some(p => p.test(memory.content));
104
+ return ((hasActionVerb ? 0.4 : 0) +
105
+ (hasInstruction ? 0.3 : 0) +
106
+ (hasConditional ? 0.3 : 0));
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // Uniqueness
110
+ // ---------------------------------------------------------------------------
111
+ /**
112
+ * Estimate similarity between two memories using tag overlap and
113
+ * summary keyword overlap as a lightweight proxy.
114
+ *
115
+ * Returns a value in [0, 1] where 1.0 = identical.
116
+ */
117
+ function estimateSimilarity(a, b) {
118
+ const tagsA = new Set(Array.isArray(a.tags) ? a.tags : []);
119
+ const tagsB = new Set(Array.isArray(b.tags) ? b.tags : []);
120
+ const tagIntersection = [...tagsA].filter(t => tagsB.has(t)).length;
121
+ const tagUnion = new Set([...tagsA, ...tagsB]).size;
122
+ const tagOverlap = tagUnion > 0 ? tagIntersection / tagUnion : 0;
123
+ // Summary keyword overlap
124
+ const wordsA = new Set(a.summary.toLowerCase().split(/\s+/).filter(w => w.length > 3));
125
+ const wordsB = new Set(b.summary.toLowerCase().split(/\s+/).filter(w => w.length > 3));
126
+ const wordIntersection = [...wordsA].filter(w => wordsB.has(w)).length;
127
+ const wordUnion = new Set([...wordsA, ...wordsB]).size;
128
+ const wordOverlap = wordUnion > 0 ? wordIntersection / wordUnion : 0;
129
+ // Weighted combination: tags carry more signal than summary keywords
130
+ return tagOverlap * 0.6 + wordOverlap * 0.4;
131
+ }
132
+ function scoreUniqueness(memory, allMemories) {
133
+ if (!allMemories || allMemories.length === 0)
134
+ return 0.5;
135
+ const others = allMemories.filter(m => m.id !== memory.id);
136
+ if (others.length === 0)
137
+ return 1.0;
138
+ const maxSimilarity = Math.max(...others.map(o => estimateSimilarity(memory, o)));
139
+ return Math.max(0.0, 1.0 - maxSimilarity);
140
+ }
141
+ // ---------------------------------------------------------------------------
142
+ // Freshness
143
+ // ---------------------------------------------------------------------------
144
+ function scoreFreshness(memory) {
145
+ const ref = memory.last_accessed_at ?? memory.created_at;
146
+ if (!ref)
147
+ return 0.1;
148
+ const normalized = /[Zz]$|[+-]\d{2}:\d{2}$/.test(ref) ? ref : ref + 'Z';
149
+ const refMs = new Date(normalized).getTime();
150
+ if (isNaN(refMs))
151
+ return 0.1;
152
+ const hoursSince = (Date.now() - refMs) / (1000 * 60 * 60);
153
+ if (hoursSince <= 24)
154
+ return 1.0;
155
+ if (hoursSince <= 168)
156
+ return 0.7; // 1 week
157
+ if (hoursSince <= 720)
158
+ return 0.4; // 1 month
159
+ return 0.2;
160
+ }
161
+ // ---------------------------------------------------------------------------
162
+ // Public API
163
+ // ---------------------------------------------------------------------------
164
+ /**
165
+ * Calculate the quality score for a single memory.
166
+ *
167
+ * If allMemories is provided, uniqueness is computed against those peers.
168
+ * Without it, uniqueness defaults to 0.5 (neutral).
169
+ */
170
+ export function calculateQuality(memory, allMemories) {
171
+ const specificity = scoreSpecificity(memory);
172
+ const actionability = scoreActionability(memory);
173
+ const uniqueness = scoreUniqueness(memory, allMemories);
174
+ const freshness = scoreFreshness(memory);
175
+ const overall = 0.30 * specificity +
176
+ 0.25 * actionability +
177
+ 0.25 * uniqueness +
178
+ 0.20 * freshness;
179
+ return {
180
+ overall: Math.min(1.0, Math.max(0.0, overall)),
181
+ specificity: Math.min(1.0, Math.max(0.0, specificity)),
182
+ actionability: Math.min(1.0, Math.max(0.0, actionability)),
183
+ uniqueness: Math.min(1.0, Math.max(0.0, uniqueness)),
184
+ freshness: Math.min(1.0, Math.max(0.0, freshness)),
185
+ };
186
+ }
187
+ /**
188
+ * Score every non-deleted memory in a project and persist the results.
189
+ * Returns aggregate stats.
190
+ */
191
+ export function scoreAllMemories(project) {
192
+ const memories = getMemoriesByProject(project);
193
+ if (memories.length === 0)
194
+ return { scored: 0, avgQuality: 0 };
195
+ let totalQuality = 0;
196
+ for (const memory of memories) {
197
+ const score = calculateQuality(memory, memories);
198
+ updateQualityScore(memory.id, score.overall);
199
+ totalQuality += score.overall;
200
+ }
201
+ return {
202
+ scored: memories.length,
203
+ avgQuality: totalQuality / memories.length,
204
+ };
205
+ }
206
+ /**
207
+ * Generate feedback tips for a newly stored memory.
208
+ *
209
+ * Returns null when the memory is already high quality (overall >= 0.7).
210
+ * Otherwise returns up to 2 actionable tips (in Korean, matching project locale).
211
+ */
212
+ export function getQualityFeedback(quality) {
213
+ if (quality.overall >= 0.7)
214
+ return null;
215
+ const tips = [];
216
+ if (quality.specificity < 0.3) {
217
+ tips.push('이 기억을 더 구체적으로 작성하면 좋겠습니다. 구체적인 이름, 버전, 경로 등을 포함해주세요.');
218
+ }
219
+ if (quality.actionability < 0.3) {
220
+ tips.push('어떻게 행동해야 하는지 구체적인 단계를 포함하면 더 유용합니다.');
221
+ }
222
+ if (quality.uniqueness < 0.3) {
223
+ tips.push('유사한 기억이 이미 존재합니다. 기존 기억을 업데이트하는 것을 고려해주세요.');
224
+ }
225
+ if (tips.length === 0)
226
+ return null;
227
+ // Return at most 2 tips
228
+ return tips.slice(0, 2).join(' ');
229
+ }
230
+ /**
231
+ * Return a distance multiplier based on quality.
232
+ *
233
+ * Low-quality memories drift outward to make room for high-quality ones.
234
+ * - quality >= 0.7 : 1.0 (no adjustment)
235
+ * - quality 0.4–0.7: 1.2 (slightly further)
236
+ * - quality < 0.4 : 1.5 (noticeably further)
237
+ */
238
+ export function qualityOrbitAdjustment(quality) {
239
+ if (quality >= 0.7)
240
+ return 1.0;
241
+ if (quality >= 0.4)
242
+ return 1.2;
243
+ return 1.5;
244
+ }
245
+ //# sourceMappingURL=quality.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality.js","sourceRoot":"","sources":["../../src/engine/quality.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * engine/repository.ts — MemoryRepository abstraction
3
+ *
4
+ * Defines the storage contract for memories and sun state.
5
+ * The engine and use-case layers depend only on this interface,
6
+ * making the concrete storage backend swappable (SQLite today,
7
+ * Python v1 or a remote API tomorrow).
8
+ *
9
+ * Dependency rule: this file lives in the engine layer and MUST NOT
10
+ * import from storage/, infrastructure, or framework packages.
11
+ */
12
+ import type { Memory, MemoryType, SunState, OrbitChange } from './types.js';
13
+ export interface InsertMemoryData {
14
+ id?: string;
15
+ project: string;
16
+ content: string;
17
+ summary?: string;
18
+ type?: MemoryType;
19
+ tags?: string[];
20
+ distance?: number;
21
+ importance?: number;
22
+ velocity?: number;
23
+ impact?: number;
24
+ access_count?: number;
25
+ last_accessed_at?: string | null;
26
+ metadata?: Record<string, unknown>;
27
+ source?: string | null;
28
+ source_path?: string | null;
29
+ source_hash?: string | null;
30
+ content_hash?: string | null;
31
+ created_at?: string;
32
+ updated_at?: string;
33
+ deleted_at?: string | null;
34
+ }
35
+ export interface MemoryRepository {
36
+ /** Persist a new memory and return the fully-resolved record. */
37
+ insert(data: InsertMemoryData): Memory;
38
+ /** Fetch a single memory by its UUID. Returns undefined if not found. */
39
+ getById(id: string): Memory | undefined;
40
+ /**
41
+ * Fetch multiple memories by a list of IDs in a single round-trip.
42
+ * Soft-deleted memories are excluded.
43
+ */
44
+ getByIds(ids: string[]): Memory[];
45
+ /**
46
+ * Fetch all non-deleted memories for a project, ordered by distance ASC.
47
+ */
48
+ getByProject(project: string): Memory[];
49
+ /**
50
+ * Find a non-deleted memory with the given content hash (SHA-256).
51
+ * Used for content-level deduplication in createMemory().
52
+ */
53
+ getByContentHash(project: string, hash: string): Memory | undefined;
54
+ /**
55
+ * Full-text search over memories in a project.
56
+ * Implementations may use FTS5, Postgres full-text, or any other backend.
57
+ */
58
+ search(project: string, query: string, limit: number): Memory[];
59
+ /** Increment access_count and refresh last_accessed_at. */
60
+ updateAccess(id: string): void;
61
+ /** Persist new orbital position after a physics recalculation. */
62
+ updateOrbit(id: string, distance: number, importance: number, velocity: number): void;
63
+ /** Soft-delete a memory (sets deleted_at; keeps the row for audit trails). */
64
+ softDelete(id: string): void;
65
+ /**
66
+ * Return up to `limit` non-deleted memories for the project ordered by
67
+ * distance ASC (closest to the sun first).
68
+ */
69
+ getNearestMemories(project: string, limit: number): Memory[];
70
+ /** Retrieve the sun (working context) for a project. */
71
+ getSunState(project: string): SunState | undefined;
72
+ /** Insert or update the sun state for a project. */
73
+ upsertSunState(state: SunState): void;
74
+ /** Append an orbit-change event for audit / analytics. */
75
+ insertOrbitLog(change: OrbitChange): void;
76
+ /** Remove orbit log entries older than `daysOld` days. */
77
+ cleanupOrbitLog(daysOld: number): void;
78
+ }
79
+ //# sourceMappingURL=repository.d.ts.map