hypercore-cli 1.1.2 → 1.4.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 (162) hide show
  1. package/LICENSE +92 -21
  2. package/README.md +8 -1
  3. package/dist/App-YMX7FSXR.js +1 -0
  4. package/dist/api-ELP6F4NC.js +1 -0
  5. package/dist/auth-SICBMA3P.js +1 -0
  6. package/dist/auth-X6CUT3DW.js +1 -0
  7. package/dist/background-ACODXSUG.js +1 -0
  8. package/dist/backlog-JD2IM336.js +1 -0
  9. package/dist/chunk-2QI2IU2V.js +1 -0
  10. package/dist/chunk-3KFRDIPQ.js +1 -0
  11. package/dist/chunk-42C5J7PN.js +1 -0
  12. package/dist/chunk-4D7XVJ7Q.js +1 -0
  13. package/dist/chunk-5KUSGQP2.js +1 -0
  14. package/dist/chunk-5OEFAGD5.js +1 -0
  15. package/dist/chunk-AUQ64BK2.js +1 -0
  16. package/dist/chunk-AV244H5C.js +1 -0
  17. package/dist/chunk-BQVBEFS4.js +1 -0
  18. package/dist/chunk-BYWQLFP2.js +1 -0
  19. package/dist/chunk-C6YL7UHE.js +1 -0
  20. package/dist/chunk-COITWWZJ.js +1 -0
  21. package/dist/chunk-CR7UUJVX.js +1 -0
  22. package/dist/chunk-E3MULLBX.js +1 -0
  23. package/dist/chunk-EZHYVJGQ.js +1 -0
  24. package/dist/chunk-FAMURNNH.js +1 -0
  25. package/dist/chunk-FGP56E4W.js +1 -0
  26. package/dist/chunk-FHGATV5B.js +1 -0
  27. package/dist/chunk-I2G27Y5P.js +1 -0
  28. package/dist/chunk-IKF43TX2.js +1 -0
  29. package/dist/chunk-INSPHCBN.js +1 -0
  30. package/dist/chunk-JHMV366T.js +1 -0
  31. package/dist/chunk-L52HX5SX.js +1 -0
  32. package/dist/chunk-LQMDUKIE.js +1 -0
  33. package/dist/chunk-M3MTKGA5.js +1 -0
  34. package/dist/chunk-MPO54FU3.js +1 -0
  35. package/dist/chunk-PVKCZI6A.js +1 -0
  36. package/dist/chunk-Q7KEPCYL.js +1 -0
  37. package/dist/chunk-ROBZ6PAL.js +1 -0
  38. package/dist/chunk-RXB5BS2N.js +1 -0
  39. package/dist/chunk-RZ3HNYMT.js +1 -0
  40. package/dist/chunk-UCGLRMTG.js +1 -0
  41. package/dist/chunk-UEHJVRKB.js +1 -0
  42. package/dist/chunk-UZYX5GGF.js +1 -0
  43. package/dist/chunk-XQJBB725.js +1 -0
  44. package/dist/chunk-YXCRL6K3.js +1 -0
  45. package/dist/claude-US2QPRBA.js +1 -0
  46. package/dist/commands-EKPWCB3T.js +1 -0
  47. package/dist/commands-GYNKP5WV.js +1 -0
  48. package/dist/commands-QHJLREPM.js +1 -0
  49. package/dist/config-2OUL5FLS.js +1 -0
  50. package/dist/config-loader-N7IBWN2P.js +1 -0
  51. package/dist/diagnose-NLHN4SAJ.js +1 -0
  52. package/dist/display-RYAW2GFB.js +1 -0
  53. package/dist/extractor-3KTM2IUL.js +1 -0
  54. package/dist/feature-flag-VVIF5FJG.js +1 -0
  55. package/dist/history-3R2UHRDQ.js +1 -0
  56. package/dist/index.js +1 -402
  57. package/dist/instance-registry-I5AIVJE2.js +1 -0
  58. package/dist/keybindings-RN3A7CRW.js +1 -0
  59. package/dist/loader-3IKPXP4R.js +1 -0
  60. package/dist/network-K5HRJE44.js +1 -0
  61. package/dist/notify-O6FNVHC4.js +1 -0
  62. package/dist/openai-compat-IPCMINVF.js +1 -0
  63. package/dist/panels-2R5YEFXH.js +1 -0
  64. package/dist/permissions-5O7KVAXU.js +1 -0
  65. package/dist/prompt-VWFPFM4N.js +1 -0
  66. package/dist/quality-GPQD25UL.js +1 -0
  67. package/dist/repl-CL4SYHU4.js +1 -0
  68. package/dist/roadmap-QRZODSNJ.js +1 -0
  69. package/dist/server-PKOHK5M2.js +1 -0
  70. package/dist/session-5HDDQQP6.js +1 -0
  71. package/dist/skills-DXWSVJSU.js +1 -0
  72. package/dist/store-WXXTKTTL.js +1 -0
  73. package/dist/team-N2GF4YYS.js +1 -0
  74. package/dist/telemetry-NT4UZLBS.js +1 -0
  75. package/dist/test-runner-F6B6RH3S.js +1 -0
  76. package/dist/theme-JJJ6ABR2.js +1 -0
  77. package/dist/upgrade-RUG3R7R5.js +1 -0
  78. package/dist/verify-6OGRY2PR.js +1 -0
  79. package/dist/version-4RHTDUNQ.js +1 -0
  80. package/dist/web/static/app.js +1 -562
  81. package/dist/web/static/index.html +114 -126
  82. package/dist/web/static/mirror.css +1 -1001
  83. package/dist/web/static/mirror.html +155 -178
  84. package/dist/web/static/mirror.js +1 -1125
  85. package/dist/web/static/onboard.css +1 -302
  86. package/dist/web/static/onboard.html +121 -145
  87. package/dist/web/static/onboard.js +1 -300
  88. package/dist/web/static/style.css +1 -602
  89. package/dist/web/static/utils.js +1 -0
  90. package/dist/web/static/workspace.css +1 -1568
  91. package/dist/web/static/workspace.html +369 -402
  92. package/dist/web/static/workspace.js +1 -1683
  93. package/dist/web-CIC7ZKBM.js +1 -0
  94. package/package.json +26 -4
  95. package/dist/api-JHHOZTL6.js +0 -162
  96. package/dist/auth-5QFJLW7J.js +0 -21
  97. package/dist/background-2EGCAAQH.js +0 -14
  98. package/dist/backlog-Q2NZCLNY.js +0 -24
  99. package/dist/chunk-2CMSCWQW.js +0 -162
  100. package/dist/chunk-4DVYJAJL.js +0 -57
  101. package/dist/chunk-5GDYH676.js +0 -271
  102. package/dist/chunk-5NLVGLD7.js +0 -66
  103. package/dist/chunk-6XTEAFZQ.js +0 -575
  104. package/dist/chunk-AQBSMYLT.js +0 -2025
  105. package/dist/chunk-BE46C7JW.js +0 -46
  106. package/dist/chunk-CLKIMCXZ.js +0 -139
  107. package/dist/chunk-DN4ASQ26.js +0 -167
  108. package/dist/chunk-DUWREZXK.js +0 -173
  109. package/dist/chunk-FCW3K6F2.js +0 -263
  110. package/dist/chunk-GFORWAMW.js +0 -251
  111. package/dist/chunk-GH7E2OJE.js +0 -223
  112. package/dist/chunk-GU2FZQ6A.js +0 -69
  113. package/dist/chunk-I7WI3BMB.js +0 -161
  114. package/dist/chunk-IOPKN5GD.js +0 -190
  115. package/dist/chunk-LBVHDGZE.js +0 -133
  116. package/dist/chunk-MGLJ53QN.js +0 -219
  117. package/dist/chunk-NETIY5UB.js +0 -134
  118. package/dist/chunk-NP47L7LG.js +0 -288
  119. package/dist/chunk-O6MG7TOH.js +0 -58
  120. package/dist/chunk-OPZYEVYR.js +0 -150
  121. package/dist/chunk-R3GPQC7I.js +0 -393
  122. package/dist/chunk-R5T3A2NQ.js +0 -166
  123. package/dist/chunk-RKB2JOV2.js +0 -43
  124. package/dist/chunk-RNG3K465.js +0 -80
  125. package/dist/chunk-TGTYKBGC.js +0 -86
  126. package/dist/chunk-UCX4VZCT.js +0 -681
  127. package/dist/chunk-WHLVZCQY.js +0 -245
  128. package/dist/chunk-Y6HMJZDJ.js +0 -1505
  129. package/dist/chunk-ZSBHUGWR.js +0 -262
  130. package/dist/claude-4BX3MJSK.js +0 -12
  131. package/dist/commands-2X4OB5RF.js +0 -128
  132. package/dist/commands-GLBCEVQK.js +0 -1044
  133. package/dist/commands-IINRNBYX.js +0 -232
  134. package/dist/config-RSNQJQPS.js +0 -8
  135. package/dist/config-loader-SXO674TF.js +0 -24
  136. package/dist/diagnose-7UPLS7I4.js +0 -12
  137. package/dist/display-IIUBEYWN.js +0 -58
  138. package/dist/extractor-D3XWOAXI.js +0 -129
  139. package/dist/history-6I6FADAU.js +0 -180
  140. package/dist/index.d.ts +0 -1
  141. package/dist/instance-registry-J7UJ7U4Z.js +0 -15
  142. package/dist/keybindings-PDXIOV3O.js +0 -15
  143. package/dist/loader-GKEYT6Y7.js +0 -58
  144. package/dist/network-JYGHQXAR.js +0 -279
  145. package/dist/notify-HPTALZDC.js +0 -14
  146. package/dist/openai-compat-R7EKWG6Z.js +0 -12
  147. package/dist/permissions-JUKXMNDH.js +0 -10
  148. package/dist/prompt-UWHSZU4P.js +0 -166
  149. package/dist/quality-ST7PPNFR.js +0 -16
  150. package/dist/repl-QHIZ2JGF.js +0 -3374
  151. package/dist/roadmap-5OBEKROY.js +0 -17
  152. package/dist/server-HCNIP7ZQ.js +0 -57
  153. package/dist/session-5EBECDUP.js +0 -21
  154. package/dist/skills-HBQQTYO4.js +0 -175
  155. package/dist/store-FKUTR7GW.js +0 -25
  156. package/dist/team-7BBBP5YQ.js +0 -385
  157. package/dist/telemetry-6R4EIE6O.js +0 -30
  158. package/dist/test-runner-AUAGIBNM.js +0 -619
  159. package/dist/theme-3SYJ3UQA.js +0 -14
  160. package/dist/upgrade-MZFH7OCN.js +0 -83
  161. package/dist/verify-JUDKTPKZ.js +0 -14
  162. package/dist/web-KS3FUGJA.js +0 -39
@@ -1,263 +0,0 @@
1
- import {
2
- hookManager
3
- } from "./chunk-CLKIMCXZ.js";
4
-
5
- // src/llm/claude.ts
6
- import Anthropic from "@anthropic-ai/sdk";
7
- function createLLMClient(config) {
8
- const options = {
9
- apiKey: config.apiKey
10
- };
11
- if (config.baseURL) {
12
- options.baseURL = config.baseURL;
13
- }
14
- return new Anthropic(options);
15
- }
16
- function buildMessages(history, userPrompt) {
17
- const messages = [];
18
- if (history && history.length > 0) {
19
- for (const msg of history) {
20
- messages.push({
21
- role: msg.role,
22
- content: msg.content
23
- });
24
- }
25
- }
26
- messages.push({ role: "user", content: userPrompt });
27
- return messages;
28
- }
29
- async function triggerToolHook(event, context) {
30
- try {
31
- return await hookManager.trigger(event, context);
32
- } catch {
33
- return { intercepted: false };
34
- }
35
- }
36
- async function callLLM(client, options) {
37
- const {
38
- systemPrompt,
39
- userPrompt,
40
- tools,
41
- model = "claude-sonnet-4-20250514",
42
- maxTokens = 8192,
43
- history,
44
- onText,
45
- onToolCall,
46
- onThinking,
47
- onToolCallDone
48
- } = options;
49
- const toolDefinitions = tools.map((t) => ({
50
- name: t.definition.name,
51
- description: t.definition.description,
52
- input_schema: t.definition.input_schema
53
- }));
54
- const messages = buildMessages(history, userPrompt);
55
- let totalOutput = "";
56
- let currentRoundText = "";
57
- const allToolCalls = [];
58
- let totalInputTokens = 0;
59
- let totalOutputTokens = 0;
60
- let maxIterations = 10;
61
- while (maxIterations-- > 0) {
62
- currentRoundText = "";
63
- const response = await client.messages.create({
64
- model,
65
- max_tokens: maxTokens,
66
- system: systemPrompt,
67
- messages,
68
- tools: toolDefinitions.length > 0 ? toolDefinitions : void 0
69
- });
70
- totalInputTokens += response.usage.input_tokens;
71
- totalOutputTokens += response.usage.output_tokens;
72
- let hasToolUse = false;
73
- let thinkingEmitted = false;
74
- const toolResults = [];
75
- for (const block of response.content) {
76
- if (block.type === "text") {
77
- totalOutput += block.text;
78
- currentRoundText += block.text;
79
- onText?.(block.text);
80
- } else if (block.type === "tool_use") {
81
- hasToolUse = true;
82
- if (!thinkingEmitted && currentRoundText.trim()) {
83
- onThinking?.(currentRoundText.trim());
84
- thinkingEmitted = true;
85
- }
86
- const tool = tools.find((t) => t.definition.name === block.name);
87
- const toolInput = block.input;
88
- const toolInputText = JSON.stringify(toolInput);
89
- onToolCall?.(block.name, toolInput);
90
- let result;
91
- const toolStart = Date.now();
92
- const hookResult = await triggerToolHook("onToolCall", {
93
- model,
94
- toolName: block.name,
95
- toolInput: toolInputText
96
- });
97
- if (hookResult.intercepted) {
98
- result = `Error: Hook intercepted tool call - ${hookResult.reason || "blocked"}`;
99
- } else if (tool) {
100
- try {
101
- result = await tool.handler(toolInput);
102
- } catch (err) {
103
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
104
- }
105
- } else {
106
- result = `Error: Tool "${block.name}" not found`;
107
- }
108
- const toolDuration = Date.now() - toolStart;
109
- onToolCallDone?.(block.name, toolDuration);
110
- await triggerToolHook("onToolResult", {
111
- model,
112
- toolName: block.name,
113
- toolInput: toolInputText,
114
- toolResult: result
115
- });
116
- allToolCalls.push({
117
- toolName: block.name,
118
- input: toolInput,
119
- output: result
120
- });
121
- toolResults.push({
122
- type: "tool_result",
123
- tool_use_id: block.id,
124
- content: result
125
- });
126
- }
127
- }
128
- if (hasToolUse) {
129
- messages.push({ role: "assistant", content: response.content });
130
- messages.push({ role: "user", content: toolResults });
131
- } else {
132
- break;
133
- }
134
- if (response.stop_reason === "end_turn") break;
135
- }
136
- return {
137
- output: totalOutput,
138
- responseText: allToolCalls.length > 0 ? currentRoundText : totalOutput,
139
- toolCalls: allToolCalls,
140
- tokenUsage: {
141
- inputTokens: totalInputTokens,
142
- outputTokens: totalOutputTokens
143
- }
144
- };
145
- }
146
- async function streamCallLLM(client, options) {
147
- const {
148
- systemPrompt,
149
- userPrompt,
150
- tools,
151
- model = "claude-sonnet-4-20250514",
152
- maxTokens = 8192,
153
- history,
154
- onText,
155
- onToolCall,
156
- onThinking,
157
- onToolCallDone
158
- } = options;
159
- const toolDefinitions = tools.map((t) => ({
160
- name: t.definition.name,
161
- description: t.definition.description,
162
- input_schema: t.definition.input_schema
163
- }));
164
- const messages = buildMessages(history, userPrompt);
165
- let totalOutput = "";
166
- let currentRoundText = "";
167
- const allToolCalls = [];
168
- let totalInputTokens = 0;
169
- let totalOutputTokens = 0;
170
- let maxIterations = 10;
171
- while (maxIterations-- > 0) {
172
- currentRoundText = "";
173
- const stream = client.messages.stream({
174
- model,
175
- max_tokens: maxTokens,
176
- system: systemPrompt,
177
- messages,
178
- tools: toolDefinitions.length > 0 ? toolDefinitions : void 0
179
- });
180
- stream.on("text", (text) => {
181
- totalOutput += text;
182
- currentRoundText += text;
183
- onText?.(text);
184
- });
185
- const response = await stream.finalMessage();
186
- totalInputTokens += response.usage.input_tokens;
187
- totalOutputTokens += response.usage.output_tokens;
188
- let hasToolUse = false;
189
- let thinkingEmitted = false;
190
- const toolResults = [];
191
- for (const block of response.content) {
192
- if (block.type === "tool_use") {
193
- hasToolUse = true;
194
- if (!thinkingEmitted && currentRoundText.trim()) {
195
- onThinking?.(currentRoundText.trim());
196
- thinkingEmitted = true;
197
- }
198
- const toolInput = block.input;
199
- const toolInputText = JSON.stringify(toolInput);
200
- onToolCall?.(block.name, toolInput);
201
- const tool = tools.find((t) => t.definition.name === block.name);
202
- let result;
203
- const toolStart = Date.now();
204
- const hookResult = await triggerToolHook("onToolCall", {
205
- model,
206
- toolName: block.name,
207
- toolInput: toolInputText
208
- });
209
- if (hookResult.intercepted) {
210
- result = `Error: Hook intercepted tool call - ${hookResult.reason || "blocked"}`;
211
- } else if (tool) {
212
- try {
213
- result = await tool.handler(toolInput);
214
- } catch (err) {
215
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
216
- }
217
- } else {
218
- result = `Error: Tool "${block.name}" not found`;
219
- }
220
- const toolDuration = Date.now() - toolStart;
221
- onToolCallDone?.(block.name, toolDuration);
222
- await triggerToolHook("onToolResult", {
223
- model,
224
- toolName: block.name,
225
- toolInput: toolInputText,
226
- toolResult: result
227
- });
228
- allToolCalls.push({
229
- toolName: block.name,
230
- input: toolInput,
231
- output: result
232
- });
233
- toolResults.push({
234
- type: "tool_result",
235
- tool_use_id: block.id,
236
- content: result
237
- });
238
- }
239
- }
240
- if (hasToolUse) {
241
- messages.push({ role: "assistant", content: response.content });
242
- messages.push({ role: "user", content: toolResults });
243
- } else {
244
- break;
245
- }
246
- if (response.stop_reason === "end_turn") break;
247
- }
248
- return {
249
- output: totalOutput,
250
- responseText: allToolCalls.length > 0 ? currentRoundText : totalOutput,
251
- toolCalls: allToolCalls,
252
- tokenUsage: {
253
- inputTokens: totalInputTokens,
254
- outputTokens: totalOutputTokens
255
- }
256
- };
257
- }
258
-
259
- export {
260
- createLLMClient,
261
- callLLM,
262
- streamCallLLM
263
- };
@@ -1,251 +0,0 @@
1
- import {
2
- HYPERCORE_DIR
3
- } from "./chunk-I7WI3BMB.js";
4
-
5
- // src/memory/store.ts
6
- import { randomBytes } from "crypto";
7
- import { readFile, writeFile, mkdir } from "fs/promises";
8
- import { existsSync } from "fs";
9
- import { join } from "path";
10
-
11
- // src/memory/types.ts
12
- var DEFAULT_EXTRACTION_CONFIG = {
13
- extractEveryNRounds: 3,
14
- maxMemoriesPerExtraction: 5,
15
- minConfidence: 0.6,
16
- maxPersonalMemories: 100,
17
- maxProjectMemories: 150,
18
- maxSystemPromptChars: 3e3
19
- };
20
- function createEmptyStore(layer) {
21
- return {
22
- version: "1",
23
- layer,
24
- records: [],
25
- totalExtractions: 0,
26
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
27
- };
28
- }
29
- var CATEGORY_LABELS = {
30
- preference: "\u504F\u597D",
31
- decision: "\u51B3\u7B56",
32
- entity: "\u5B9E\u4F53",
33
- pattern: "\u6A21\u5F0F",
34
- fact: "\u4E8B\u5B9E",
35
- convention: "\u7EA6\u5B9A",
36
- insight: "\u6D1E\u5BDF",
37
- manual: "\u624B\u52A8"
38
- };
39
- var CATEGORY_MD_HEADERS = {
40
- preference: "User Preferences",
41
- decision: "Key Decisions",
42
- entity: "Named Entities",
43
- pattern: "Patterns & Solutions",
44
- fact: "Known Facts",
45
- convention: "Conventions",
46
- insight: "Team Insights",
47
- manual: "User Notes"
48
- };
49
-
50
- // src/memory/store.ts
51
- function memoryDir(layer, opts) {
52
- switch (layer) {
53
- case "personal":
54
- return join(HYPERCORE_DIR, "memory");
55
- case "project":
56
- return join(opts?.projectPath || process.cwd(), ".hypercore", "memory");
57
- case "team":
58
- if (!opts?.teamId) throw new Error("team layer requires teamId");
59
- return join(HYPERCORE_DIR, "teams", opts.teamId, "memory");
60
- }
61
- }
62
- function jsonPath(layer, opts) {
63
- return join(memoryDir(layer, opts), `${layer}.json`);
64
- }
65
- function mdPath(layer, opts) {
66
- return join(memoryDir(layer, opts), `${layer}.md`);
67
- }
68
- function generateMemoryId() {
69
- const ts = Date.now();
70
- const rand = randomBytes(2).toString("hex");
71
- return `mem_${ts}_${rand}`;
72
- }
73
- async function loadStore(layer, opts) {
74
- const fp = jsonPath(layer, opts);
75
- if (!existsSync(fp)) return createEmptyStore(layer);
76
- try {
77
- const raw = await readFile(fp, "utf-8");
78
- return JSON.parse(raw);
79
- } catch {
80
- return createEmptyStore(layer);
81
- }
82
- }
83
- async function saveStore(store, opts) {
84
- const dir = memoryDir(store.layer, opts);
85
- await mkdir(dir, { recursive: true });
86
- store.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
87
- await writeFile(jsonPath(store.layer, opts), JSON.stringify(store, null, 2), "utf-8");
88
- await rebuildMarkdown(store, opts);
89
- }
90
- async function addMemory(layer, record, opts) {
91
- const store = await loadStore(layer, opts);
92
- const config = DEFAULT_EXTRACTION_CONFIG;
93
- const maxRecords = layer === "personal" ? config.maxPersonalMemories : config.maxProjectMemories;
94
- const existingIdx = findDuplicate(store.records, record.content);
95
- if (existingIdx >= 0) {
96
- const existing = store.records[existingIdx];
97
- existing.content = record.content;
98
- existing.tags = record.tags;
99
- existing.confidence = Math.max(existing.confidence, record.confidence);
100
- existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
101
- existing.accessCount++;
102
- await saveStore(store, opts);
103
- return existing;
104
- }
105
- if (store.records.length >= maxRecords) {
106
- store.records.sort((a, b) => a.confidence - b.confidence || a.updatedAt.localeCompare(b.updatedAt));
107
- store.records.shift();
108
- }
109
- const now = (/* @__PURE__ */ new Date()).toISOString();
110
- const newRecord = {
111
- ...record,
112
- id: generateMemoryId(),
113
- layer,
114
- createdAt: now,
115
- updatedAt: now,
116
- accessCount: 0
117
- };
118
- store.records.push(newRecord);
119
- await saveStore(store, opts);
120
- return newRecord;
121
- }
122
- async function removeMemory(layer, memoryId, opts) {
123
- const store = await loadStore(layer, opts);
124
- const idx = store.records.findIndex((r) => r.id === memoryId);
125
- if (idx < 0) return false;
126
- store.records.splice(idx, 1);
127
- await saveStore(store, opts);
128
- return true;
129
- }
130
- async function searchMemories(layer, query, opts) {
131
- const store = await loadStore(layer, opts);
132
- let records = store.records;
133
- if (opts?.category) {
134
- records = records.filter((r) => r.category === opts.category);
135
- }
136
- const queryLower = query.toLowerCase();
137
- const queryTerms = queryLower.split(/\s+/).filter(Boolean);
138
- const scored = records.map((record) => {
139
- const contentLower = record.content.toLowerCase();
140
- const tagsLower = record.tags.join(" ").toLowerCase();
141
- const combined = contentLower + " " + tagsLower;
142
- let matchedTerms = 0;
143
- for (const term of queryTerms) {
144
- if (combined.includes(term)) matchedTerms++;
145
- }
146
- const score = queryTerms.length > 0 ? matchedTerms / queryTerms.length : 0;
147
- return { record, score };
148
- });
149
- const results = scored.filter((r) => r.score > 0).sort((a, b) => b.score - a.score || b.record.confidence - a.record.confidence);
150
- for (const r of results) {
151
- r.record.accessCount++;
152
- r.record.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
153
- }
154
- if (results.length > 0) {
155
- await saveStore(store, opts);
156
- }
157
- return results.slice(0, opts?.limit || 20);
158
- }
159
- async function listMemories(layer, opts) {
160
- const store = await loadStore(layer, opts);
161
- return [...store.records].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
162
- }
163
- async function getMemoryStats(layer, opts) {
164
- const store = await loadStore(layer, opts);
165
- const byCategory = {};
166
- for (const r of store.records) {
167
- byCategory[r.category] = (byCategory[r.category] || 0) + 1;
168
- }
169
- return {
170
- total: store.records.length,
171
- byCategory,
172
- lastExtractionAt: store.lastExtractionAt,
173
- totalExtractions: store.totalExtractions
174
- };
175
- }
176
- async function markExtraction(layer, opts) {
177
- const store = await loadStore(layer, opts);
178
- store.lastExtractionAt = (/* @__PURE__ */ new Date()).toISOString();
179
- store.totalExtractions++;
180
- await saveStore(store, opts);
181
- }
182
- function findDuplicate(records, content) {
183
- const normalized = content.toLowerCase().replace(/\s+/g, " ").trim();
184
- for (let i = 0; i < records.length; i++) {
185
- const existing = records[i].content.toLowerCase().replace(/\s+/g, " ").trim();
186
- if (similarityRatio(existing, normalized) > 0.8) return i;
187
- }
188
- return -1;
189
- }
190
- function similarityRatio(a, b) {
191
- if (a === b) return 1;
192
- if (!a || !b) return 0;
193
- const bigramsA = /* @__PURE__ */ new Set();
194
- for (let i = 0; i < a.length - 1; i++) bigramsA.add(a.slice(i, i + 2));
195
- const bigramsB = /* @__PURE__ */ new Set();
196
- for (let i = 0; i < b.length - 1; i++) bigramsB.add(b.slice(i, i + 2));
197
- let intersection = 0;
198
- for (const bg of bigramsA) {
199
- if (bigramsB.has(bg)) intersection++;
200
- }
201
- const union = bigramsA.size + bigramsB.size - intersection;
202
- return union > 0 ? intersection / union : 0;
203
- }
204
- async function rebuildMarkdown(store, opts) {
205
- const lines = [
206
- `--- Memory Context (${store.layer}) ---`,
207
- ""
208
- ];
209
- const grouped = {};
210
- for (const r of store.records) {
211
- if (!grouped[r.category]) grouped[r.category] = [];
212
- grouped[r.category].push(r);
213
- }
214
- for (const [category, records] of Object.entries(grouped)) {
215
- const header = CATEGORY_MD_HEADERS[category] || category;
216
- lines.push(`[${header}]`);
217
- const sorted = records.sort((a, b) => b.confidence - a.confidence);
218
- for (const r of sorted) {
219
- const tags = r.tags.map((t) => `#${t}`).join(" ");
220
- lines.push(`- ${r.content} ${tags}`);
221
- }
222
- lines.push("");
223
- }
224
- lines.push(`--- End Memory Context ---`);
225
- const fp = mdPath(store.layer, opts);
226
- await writeFile(fp, lines.join("\n"), "utf-8");
227
- }
228
- async function readMemoryMarkdown(layer, opts) {
229
- const fp = mdPath(layer, opts);
230
- if (!existsSync(fp)) return "";
231
- try {
232
- return await readFile(fp, "utf-8");
233
- } catch {
234
- return "";
235
- }
236
- }
237
-
238
- export {
239
- DEFAULT_EXTRACTION_CONFIG,
240
- CATEGORY_LABELS,
241
- generateMemoryId,
242
- loadStore,
243
- saveStore,
244
- addMemory,
245
- removeMemory,
246
- searchMemories,
247
- listMemories,
248
- getMemoryStats,
249
- markExtraction,
250
- readMemoryMarkdown
251
- };