@haisto/opencode-mem 2.14.3-beta.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 (152) hide show
  1. package/README.md +165 -0
  2. package/dist/config.d.ts +62 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +457 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +486 -0
  8. package/dist/plugin.d.ts +9 -0
  9. package/dist/plugin.d.ts.map +1 -0
  10. package/dist/plugin.js +5 -0
  11. package/dist/services/ai/ai-provider-factory.d.ts +8 -0
  12. package/dist/services/ai/ai-provider-factory.d.ts.map +1 -0
  13. package/dist/services/ai/ai-provider-factory.js +28 -0
  14. package/dist/services/ai/opencode-provider.d.ts +36 -0
  15. package/dist/services/ai/opencode-provider.d.ts.map +1 -0
  16. package/dist/services/ai/opencode-provider.js +92 -0
  17. package/dist/services/ai/provider-config.d.ts +17 -0
  18. package/dist/services/ai/provider-config.d.ts.map +1 -0
  19. package/dist/services/ai/provider-config.js +14 -0
  20. package/dist/services/ai/providers/anthropic-messages.d.ts +12 -0
  21. package/dist/services/ai/providers/anthropic-messages.d.ts.map +1 -0
  22. package/dist/services/ai/providers/anthropic-messages.js +184 -0
  23. package/dist/services/ai/providers/base-provider.d.ts +25 -0
  24. package/dist/services/ai/providers/base-provider.d.ts.map +1 -0
  25. package/dist/services/ai/providers/base-provider.js +23 -0
  26. package/dist/services/ai/providers/google-gemini.d.ts +16 -0
  27. package/dist/services/ai/providers/google-gemini.d.ts.map +1 -0
  28. package/dist/services/ai/providers/google-gemini.js +228 -0
  29. package/dist/services/ai/providers/openai-chat-completion.d.ts +14 -0
  30. package/dist/services/ai/providers/openai-chat-completion.d.ts.map +1 -0
  31. package/dist/services/ai/providers/openai-chat-completion.js +318 -0
  32. package/dist/services/ai/providers/openai-responses.d.ts +14 -0
  33. package/dist/services/ai/providers/openai-responses.d.ts.map +1 -0
  34. package/dist/services/ai/providers/openai-responses.js +182 -0
  35. package/dist/services/ai/session/ai-session-manager.d.ts +21 -0
  36. package/dist/services/ai/session/ai-session-manager.d.ts.map +1 -0
  37. package/dist/services/ai/session/ai-session-manager.js +166 -0
  38. package/dist/services/ai/session/session-types.d.ts +43 -0
  39. package/dist/services/ai/session/session-types.d.ts.map +1 -0
  40. package/dist/services/ai/session/session-types.js +1 -0
  41. package/dist/services/ai/tools/tool-schema.d.ts +41 -0
  42. package/dist/services/ai/tools/tool-schema.d.ts.map +1 -0
  43. package/dist/services/ai/tools/tool-schema.js +24 -0
  44. package/dist/services/ai/validators/user-profile-validator.d.ts +13 -0
  45. package/dist/services/ai/validators/user-profile-validator.d.ts.map +1 -0
  46. package/dist/services/ai/validators/user-profile-validator.js +111 -0
  47. package/dist/services/api-handlers.d.ts +164 -0
  48. package/dist/services/api-handlers.d.ts.map +1 -0
  49. package/dist/services/api-handlers.js +927 -0
  50. package/dist/services/auto-capture.d.ts +3 -0
  51. package/dist/services/auto-capture.d.ts.map +1 -0
  52. package/dist/services/auto-capture.js +309 -0
  53. package/dist/services/cleanup-service.d.ts +23 -0
  54. package/dist/services/cleanup-service.d.ts.map +1 -0
  55. package/dist/services/cleanup-service.js +102 -0
  56. package/dist/services/client.d.ts +119 -0
  57. package/dist/services/client.d.ts.map +1 -0
  58. package/dist/services/client.js +257 -0
  59. package/dist/services/context.d.ts +11 -0
  60. package/dist/services/context.d.ts.map +1 -0
  61. package/dist/services/context.js +34 -0
  62. package/dist/services/deduplication-service.d.ts +30 -0
  63. package/dist/services/deduplication-service.d.ts.map +1 -0
  64. package/dist/services/deduplication-service.js +124 -0
  65. package/dist/services/embedding.d.ts +15 -0
  66. package/dist/services/embedding.d.ts.map +1 -0
  67. package/dist/services/embedding.js +127 -0
  68. package/dist/services/jsonc.d.ts +7 -0
  69. package/dist/services/jsonc.d.ts.map +1 -0
  70. package/dist/services/jsonc.js +76 -0
  71. package/dist/services/language-detector.d.ts +3 -0
  72. package/dist/services/language-detector.d.ts.map +1 -0
  73. package/dist/services/language-detector.js +33 -0
  74. package/dist/services/logger.d.ts +11 -0
  75. package/dist/services/logger.d.ts.map +1 -0
  76. package/dist/services/logger.js +97 -0
  77. package/dist/services/migration-service.d.ts +42 -0
  78. package/dist/services/migration-service.d.ts.map +1 -0
  79. package/dist/services/migration-service.js +250 -0
  80. package/dist/services/privacy.d.ts +3 -0
  81. package/dist/services/privacy.d.ts.map +1 -0
  82. package/dist/services/privacy.js +7 -0
  83. package/dist/services/secret-resolver.d.ts +2 -0
  84. package/dist/services/secret-resolver.d.ts.map +1 -0
  85. package/dist/services/secret-resolver.js +55 -0
  86. package/dist/services/sqlite/connection-manager.d.ts +13 -0
  87. package/dist/services/sqlite/connection-manager.d.ts.map +1 -0
  88. package/dist/services/sqlite/connection-manager.js +74 -0
  89. package/dist/services/sqlite/shard-manager.d.ts +23 -0
  90. package/dist/services/sqlite/shard-manager.d.ts.map +1 -0
  91. package/dist/services/sqlite/shard-manager.js +288 -0
  92. package/dist/services/sqlite/sqlite-bootstrap.d.ts +2 -0
  93. package/dist/services/sqlite/sqlite-bootstrap.d.ts.map +1 -0
  94. package/dist/services/sqlite/sqlite-bootstrap.js +8 -0
  95. package/dist/services/sqlite/types.d.ts +42 -0
  96. package/dist/services/sqlite/types.d.ts.map +1 -0
  97. package/dist/services/sqlite/types.js +1 -0
  98. package/dist/services/sqlite/vector-search.d.ts +29 -0
  99. package/dist/services/sqlite/vector-search.d.ts.map +1 -0
  100. package/dist/services/sqlite/vector-search.js +279 -0
  101. package/dist/services/tags.d.ts +24 -0
  102. package/dist/services/tags.d.ts.map +1 -0
  103. package/dist/services/tags.js +145 -0
  104. package/dist/services/user-memory-learning.d.ts +3 -0
  105. package/dist/services/user-memory-learning.d.ts.map +1 -0
  106. package/dist/services/user-memory-learning.js +235 -0
  107. package/dist/services/user-profile/profile-context.d.ts +2 -0
  108. package/dist/services/user-profile/profile-context.d.ts.map +1 -0
  109. package/dist/services/user-profile/profile-context.js +40 -0
  110. package/dist/services/user-profile/profile-utils.d.ts +3 -0
  111. package/dist/services/user-profile/profile-utils.d.ts.map +1 -0
  112. package/dist/services/user-profile/profile-utils.js +45 -0
  113. package/dist/services/user-profile/types.d.ts +46 -0
  114. package/dist/services/user-profile/types.d.ts.map +1 -0
  115. package/dist/services/user-profile/types.js +1 -0
  116. package/dist/services/user-profile/user-profile-manager.d.ts +23 -0
  117. package/dist/services/user-profile/user-profile-manager.d.ts.map +1 -0
  118. package/dist/services/user-profile/user-profile-manager.js +337 -0
  119. package/dist/services/user-prompt/user-prompt-manager.d.ts +41 -0
  120. package/dist/services/user-prompt/user-prompt-manager.d.ts.map +1 -0
  121. package/dist/services/user-prompt/user-prompt-manager.js +192 -0
  122. package/dist/services/vector-backends/backend-factory.d.ts +3 -0
  123. package/dist/services/vector-backends/backend-factory.d.ts.map +1 -0
  124. package/dist/services/vector-backends/backend-factory.js +104 -0
  125. package/dist/services/vector-backends/exact-scan-backend.d.ts +39 -0
  126. package/dist/services/vector-backends/exact-scan-backend.d.ts.map +1 -0
  127. package/dist/services/vector-backends/exact-scan-backend.js +63 -0
  128. package/dist/services/vector-backends/types.d.ts +51 -0
  129. package/dist/services/vector-backends/types.d.ts.map +1 -0
  130. package/dist/services/vector-backends/types.js +1 -0
  131. package/dist/services/vector-backends/usearch-backend.d.ts +47 -0
  132. package/dist/services/vector-backends/usearch-backend.d.ts.map +1 -0
  133. package/dist/services/vector-backends/usearch-backend.js +174 -0
  134. package/dist/services/web-server-worker.d.ts +2 -0
  135. package/dist/services/web-server-worker.d.ts.map +1 -0
  136. package/dist/services/web-server-worker.js +283 -0
  137. package/dist/services/web-server.d.ts +31 -0
  138. package/dist/services/web-server.d.ts.map +1 -0
  139. package/dist/services/web-server.js +356 -0
  140. package/dist/types/index.d.ts +19 -0
  141. package/dist/types/index.d.ts.map +1 -0
  142. package/dist/types/index.js +1 -0
  143. package/dist/web/app.d.ts +2 -0
  144. package/dist/web/app.d.ts.map +1 -0
  145. package/dist/web/app.js +1238 -0
  146. package/dist/web/favicon.ico +0 -0
  147. package/dist/web/i18n.d.ts +2 -0
  148. package/dist/web/i18n.d.ts.map +1 -0
  149. package/dist/web/i18n.js +312 -0
  150. package/dist/web/index.html +293 -0
  151. package/dist/web/styles.css +1786 -0
  152. package/package.json +78 -0
@@ -0,0 +1,279 @@
1
+ import { getDatabase } from "./sqlite-bootstrap.js";
2
+ import { connectionManager } from "./connection-manager.js";
3
+ import { log } from "../logger.js";
4
+ import { CONFIG } from "../../config.js";
5
+ import { createVectorBackend } from "../vector-backends/backend-factory.js";
6
+ import { ExactScanBackend } from "../vector-backends/exact-scan-backend.js";
7
+ const Database = getDatabase();
8
+ function toBlob(vector) {
9
+ return vector ? new Uint8Array(vector.buffer) : null;
10
+ }
11
+ export class VectorSearch {
12
+ backendPromise;
13
+ fallbackBackend;
14
+ constructor(backend, fallbackBackend = new ExactScanBackend()) {
15
+ this.backendPromise = backend
16
+ ? Promise.resolve(backend)
17
+ : createVectorBackend({ vectorBackend: CONFIG.vectorBackend });
18
+ this.fallbackBackend = fallbackBackend;
19
+ }
20
+ async getBackend() {
21
+ return this.backendPromise;
22
+ }
23
+ async insertVector(db, record, shard) {
24
+ const insertMemory = db.prepare(`
25
+ INSERT INTO memories (
26
+ id, content, vector, tags_vector, container_tag, tags, type, created_at, updated_at,
27
+ metadata, display_name, user_name, user_email, project_path, project_name, git_repo_url
28
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
29
+ `);
30
+ insertMemory.run(record.id, record.content, toBlob(record.vector), toBlob(record.tagsVector), record.containerTag, record.tags || null, record.type || null, record.createdAt, record.updatedAt, record.metadata || null, record.displayName || null, record.userName || null, record.userEmail || null, record.projectPath || null, record.projectName || null, record.gitRepoUrl || null);
31
+ try {
32
+ if (shard) {
33
+ const backend = await this.getBackend();
34
+ await backend.insert({ id: record.id, vector: record.vector, shard, kind: "content" });
35
+ if (record.tagsVector) {
36
+ await backend.insert({ id: record.id, vector: record.tagsVector, shard, kind: "tags" });
37
+ }
38
+ }
39
+ }
40
+ catch (error) {
41
+ db.prepare(`DELETE FROM memories WHERE id = ?`).run(record.id);
42
+ throw error;
43
+ }
44
+ }
45
+ async searchInShard(shard, queryVector, containerTag, limit, queryText) {
46
+ const db = connectionManager.getConnection(shard.dbPath);
47
+ const backend = await this.getBackend();
48
+ let contentResults;
49
+ let tagsResults;
50
+ try {
51
+ await backend.rebuildFromShard({ db, shard, kind: "content" });
52
+ await backend.rebuildFromShard({ db, shard, kind: "tags" });
53
+ contentResults = await backend.search({
54
+ db,
55
+ shard,
56
+ kind: "content",
57
+ queryVector,
58
+ limit: limit * 4,
59
+ });
60
+ tagsResults = await backend.search({
61
+ db,
62
+ shard,
63
+ kind: "tags",
64
+ queryVector,
65
+ limit: limit * 4,
66
+ });
67
+ }
68
+ catch (error) {
69
+ log("Vector search degraded to exact scan in shard", {
70
+ shardId: shard.id,
71
+ backend: backend.getBackendName(),
72
+ error: String(error),
73
+ });
74
+ await this.fallbackBackend.rebuildFromShard({ db, shard, kind: "content" });
75
+ await this.fallbackBackend.rebuildFromShard({ db, shard, kind: "tags" });
76
+ contentResults = await this.fallbackBackend.search({
77
+ db,
78
+ shard,
79
+ kind: "content",
80
+ queryVector,
81
+ limit: limit * 4,
82
+ });
83
+ tagsResults = await this.fallbackBackend.search({
84
+ db,
85
+ shard,
86
+ kind: "tags",
87
+ queryVector,
88
+ limit: limit * 4,
89
+ });
90
+ }
91
+ const scoreMap = new Map();
92
+ for (const r of contentResults) {
93
+ scoreMap.set(r.id, { contentSim: 1 - r.distance, tagsSim: 0 });
94
+ }
95
+ for (const r of tagsResults) {
96
+ const entry = scoreMap.get(r.id);
97
+ if (entry) {
98
+ entry.tagsSim = 1 - r.distance;
99
+ }
100
+ else {
101
+ scoreMap.set(r.id, { contentSim: 0, tagsSim: 1 - r.distance });
102
+ }
103
+ }
104
+ const ids = Array.from(scoreMap.keys());
105
+ if (ids.length === 0)
106
+ return [];
107
+ const placeholders = ids.map(() => "?").join(",");
108
+ const rows = db
109
+ .prepare(containerTag === ""
110
+ ? `
111
+ SELECT * FROM memories
112
+ WHERE id IN (${placeholders})
113
+ `
114
+ : `
115
+ SELECT * FROM memories
116
+ WHERE id IN (${placeholders}) AND container_tag = ?
117
+ `)
118
+ .all(...ids, ...(containerTag === "" ? [] : [containerTag]));
119
+ const queryWords = queryText
120
+ ? queryText
121
+ .toLowerCase()
122
+ .split(/[\s,]+/)
123
+ .filter((w) => w.length > 1)
124
+ : [];
125
+ const hydratedResults = rows.map((row) => {
126
+ const scores = scoreMap.get(row.id);
127
+ const memoryTagsStr = row.tags || "";
128
+ const memoryTags = memoryTagsStr.split(",").map((t) => t.trim().toLowerCase());
129
+ let exactMatchBoost = 0;
130
+ if (queryWords.length > 0 && memoryTags.length > 0) {
131
+ const matches = queryWords.filter((w) => memoryTags.some((t) => t.includes(w) || w.includes(t))).length;
132
+ exactMatchBoost = matches / Math.max(queryWords.length, 1);
133
+ }
134
+ const finalTagsSim = Math.max(scores.tagsSim, exactMatchBoost);
135
+ const similarity = scores.contentSim * 0.6 + finalTagsSim * 0.4;
136
+ return {
137
+ id: row.id,
138
+ memory: row.content,
139
+ similarity,
140
+ tags: memoryTagsStr ? memoryTagsStr.split(",") : [],
141
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
142
+ containerTag: row.container_tag,
143
+ displayName: row.display_name,
144
+ userName: row.user_name,
145
+ userEmail: row.user_email,
146
+ projectPath: row.project_path,
147
+ projectName: row.project_name,
148
+ gitRepoUrl: row.git_repo_url,
149
+ isPinned: row.is_pinned,
150
+ };
151
+ });
152
+ hydratedResults.sort((a, b) => b.similarity - a.similarity);
153
+ return hydratedResults;
154
+ }
155
+ async searchAcrossShards(shards, queryVector, containerTag, limit, similarityThreshold, queryText) {
156
+ const shardPromises = shards.map(async (shard) => {
157
+ try {
158
+ return await this.searchInShard(shard, queryVector, containerTag, limit, queryText);
159
+ }
160
+ catch (error) {
161
+ log("Shard search error", { shardId: shard.id, error: String(error) });
162
+ return [];
163
+ }
164
+ });
165
+ const resultsArray = await Promise.all(shardPromises);
166
+ const allResults = resultsArray.flat();
167
+ allResults.sort((a, b) => b.similarity - a.similarity);
168
+ return allResults.filter((r) => r.similarity >= similarityThreshold).slice(0, limit);
169
+ }
170
+ async deleteVector(db, memoryId, shard) {
171
+ db.prepare(`DELETE FROM memories WHERE id = ?`).run(memoryId);
172
+ if (shard) {
173
+ const backend = await this.getBackend();
174
+ await backend.delete({ id: memoryId, shard, kind: "content" });
175
+ await backend.delete({ id: memoryId, shard, kind: "tags" });
176
+ }
177
+ }
178
+ async updateVector(db, memoryId, vector, shard, tagsVector) {
179
+ db.prepare(`UPDATE memories SET vector = ?, tags_vector = ? WHERE id = ?`).run(toBlob(vector), toBlob(tagsVector), memoryId);
180
+ if (shard) {
181
+ const backend = await this.getBackend();
182
+ await backend.insert({ id: memoryId, vector, shard, kind: "content" });
183
+ if (tagsVector) {
184
+ await backend.insert({ id: memoryId, vector: tagsVector, shard, kind: "tags" });
185
+ }
186
+ else {
187
+ await backend.delete({ id: memoryId, shard, kind: "tags" });
188
+ }
189
+ }
190
+ }
191
+ listMemories(db, containerTag, limit) {
192
+ const stmt = db.prepare(containerTag === ""
193
+ ? `
194
+ SELECT * FROM memories
195
+ ORDER BY created_at DESC
196
+ LIMIT ?
197
+ `
198
+ : `
199
+ SELECT * FROM memories
200
+ WHERE container_tag = ?
201
+ ORDER BY created_at DESC
202
+ LIMIT ?
203
+ `);
204
+ return (containerTag === "" ? stmt.all(limit) : stmt.all(containerTag, limit));
205
+ }
206
+ getAllMemories(db) {
207
+ const stmt = db.prepare(`SELECT * FROM memories ORDER BY created_at DESC`);
208
+ return stmt.all();
209
+ }
210
+ getMemoryById(db, memoryId) {
211
+ const stmt = db.prepare(`SELECT * FROM memories WHERE id = ?`);
212
+ return stmt.get(memoryId);
213
+ }
214
+ getMemoriesBySessionID(db, sessionID) {
215
+ const stmt = db.prepare(`
216
+ SELECT * FROM memories
217
+ WHERE metadata LIKE ?
218
+ ORDER BY created_at DESC
219
+ `);
220
+ const rows = stmt.all(`%"sessionID":"${sessionID}"%`);
221
+ return rows.map((row) => ({
222
+ ...row,
223
+ tags: row.tags ? row.tags.split(",") : [],
224
+ metadata: row.metadata ? JSON.parse(row.metadata) : {},
225
+ }));
226
+ }
227
+ countVectors(db, containerTag) {
228
+ const stmt = db.prepare(`SELECT COUNT(*) as count FROM memories WHERE container_tag = ?`);
229
+ const result = stmt.get(containerTag);
230
+ return result.count;
231
+ }
232
+ countAllVectors(db) {
233
+ const stmt = db.prepare(`SELECT COUNT(*) as count FROM memories`);
234
+ const result = stmt.get();
235
+ return result.count;
236
+ }
237
+ getDistinctTags(db) {
238
+ const stmt = db.prepare(`
239
+ SELECT DISTINCT
240
+ container_tag,
241
+ display_name,
242
+ user_name,
243
+ user_email,
244
+ project_path,
245
+ project_name,
246
+ git_repo_url
247
+ FROM memories
248
+ `);
249
+ return stmt.all();
250
+ }
251
+ pinMemory(db, memoryId) {
252
+ const stmt = db.prepare(`UPDATE memories SET is_pinned = 1 WHERE id = ?`);
253
+ stmt.run(memoryId);
254
+ }
255
+ unpinMemory(db, memoryId) {
256
+ const stmt = db.prepare(`UPDATE memories SET is_pinned = 0 WHERE id = ?`);
257
+ stmt.run(memoryId);
258
+ }
259
+ async rebuildIndexForShard(db, scope, scopeHash, shardIndex) {
260
+ const backend = await this.getBackend();
261
+ const shard = {
262
+ id: 0,
263
+ scope: scope,
264
+ scopeHash,
265
+ shardIndex,
266
+ dbPath: "",
267
+ vectorCount: 0,
268
+ isActive: true,
269
+ createdAt: Date.now(),
270
+ };
271
+ await backend.rebuildFromShard({ db, shard, kind: "content" });
272
+ await backend.rebuildFromShard({ db, shard, kind: "tags" });
273
+ }
274
+ async deleteShardIndexes(shard) {
275
+ const backend = await this.getBackend();
276
+ await backend.deleteShardIndexes({ shard });
277
+ }
278
+ }
279
+ export const vectorSearch = new VectorSearch();
@@ -0,0 +1,24 @@
1
+ export interface TagInfo {
2
+ tag: string;
3
+ displayName: string;
4
+ userName?: string;
5
+ userEmail?: string;
6
+ projectPath?: string;
7
+ projectName?: string;
8
+ gitRepoUrl?: string;
9
+ }
10
+ export declare function getGitEmail(): string | null;
11
+ export declare function getGitName(): string | null;
12
+ export declare function getGitRepoUrl(directory: string): string | null;
13
+ export declare function getGitCommonDir(directory: string): string | null;
14
+ export declare function getGitTopLevel(directory: string): string | null;
15
+ export declare function getProjectRoot(directory: string): string;
16
+ export declare function getProjectIdentity(directory: string): string;
17
+ export declare function getProjectName(directory: string): string;
18
+ export declare function getUserTagInfo(): TagInfo;
19
+ export declare function getProjectTagInfo(directory: string): TagInfo;
20
+ export declare function getTags(directory: string): {
21
+ user: TagInfo;
22
+ project: TagInfo;
23
+ };
24
+ //# sourceMappingURL=tags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/services/tags.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAU3C;AAED,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAU1C;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9D;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBhE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW/D;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAYxD;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAY5D;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED,wBAAgB,cAAc,IAAI,OAAO,CAoBxC;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAa5D;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG;IAC1C,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB,CAKA"}
@@ -0,0 +1,145 @@
1
+ import { createHash } from "node:crypto";
2
+ import { execSync } from "node:child_process";
3
+ import { CONFIG } from "../config.js";
4
+ import { normalize, resolve, isAbsolute, basename, dirname } from "node:path";
5
+ import { realpathSync, existsSync } from "node:fs";
6
+ function sha256(input) {
7
+ return createHash("sha256").update(input).digest("hex").slice(0, 16);
8
+ }
9
+ export function getGitEmail() {
10
+ try {
11
+ const email = execSync("git config user.email", {
12
+ encoding: "utf-8",
13
+ stdio: ["ignore", "pipe", "ignore"],
14
+ }).trim();
15
+ return email || null;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ export function getGitName() {
22
+ try {
23
+ const name = execSync("git config user.name", {
24
+ encoding: "utf-8",
25
+ stdio: ["ignore", "pipe", "ignore"],
26
+ }).trim();
27
+ return name || null;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ export function getGitRepoUrl(directory) {
34
+ try {
35
+ const url = execSync("git config --get remote.origin.url", {
36
+ encoding: "utf-8",
37
+ cwd: directory,
38
+ stdio: ["ignore", "pipe", "ignore"],
39
+ }).trim();
40
+ return url || null;
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ export function getGitCommonDir(directory) {
47
+ try {
48
+ const commonDir = execSync("git rev-parse --git-common-dir", {
49
+ encoding: "utf-8",
50
+ cwd: directory,
51
+ stdio: ["ignore", "pipe", "ignore"],
52
+ }).trim();
53
+ if (!commonDir) {
54
+ return null;
55
+ }
56
+ const resolved = isAbsolute(commonDir)
57
+ ? normalize(commonDir)
58
+ : normalize(resolve(directory, commonDir));
59
+ if (existsSync(resolved)) {
60
+ return realpathSync(resolved);
61
+ }
62
+ return resolved;
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ export function getGitTopLevel(directory) {
69
+ try {
70
+ const topLevel = execSync("git rev-parse --show-toplevel", {
71
+ encoding: "utf-8",
72
+ cwd: directory,
73
+ stdio: ["ignore", "pipe", "ignore"],
74
+ }).trim();
75
+ return topLevel || null;
76
+ }
77
+ catch {
78
+ return null;
79
+ }
80
+ }
81
+ export function getProjectRoot(directory) {
82
+ const commonDir = getGitCommonDir(directory);
83
+ if (commonDir && basename(commonDir) === ".git") {
84
+ return dirname(commonDir);
85
+ }
86
+ const topLevel = getGitTopLevel(directory);
87
+ if (topLevel) {
88
+ return topLevel;
89
+ }
90
+ return directory;
91
+ }
92
+ export function getProjectIdentity(directory) {
93
+ const commonDir = getGitCommonDir(directory);
94
+ if (commonDir) {
95
+ return `git-common:${commonDir}`;
96
+ }
97
+ const gitRepoUrl = getGitRepoUrl(directory);
98
+ if (gitRepoUrl) {
99
+ return `remote:${gitRepoUrl}`;
100
+ }
101
+ return `path:${normalize(directory)}`;
102
+ }
103
+ export function getProjectName(directory) {
104
+ const normalized = normalize(directory).replace(/\\/g, "/");
105
+ const parts = normalized.split("/").filter((p) => p && p !== ".");
106
+ return parts[parts.length - 1] || directory;
107
+ }
108
+ export function getUserTagInfo() {
109
+ const email = CONFIG.userEmailOverride || getGitEmail();
110
+ const name = CONFIG.userNameOverride || getGitName();
111
+ if (email) {
112
+ return {
113
+ tag: `${CONFIG.containerTagPrefix}_user_${sha256(email)}`,
114
+ displayName: name || email,
115
+ userName: name || undefined,
116
+ userEmail: email,
117
+ };
118
+ }
119
+ const fallback = name || process.env.USER || process.env.USERNAME || "anonymous";
120
+ return {
121
+ tag: `${CONFIG.containerTagPrefix}_user_${sha256(fallback)}`,
122
+ displayName: fallback,
123
+ userName: fallback,
124
+ userEmail: undefined,
125
+ };
126
+ }
127
+ export function getProjectTagInfo(directory) {
128
+ const projectRoot = getProjectRoot(directory);
129
+ const projectName = getProjectName(projectRoot);
130
+ const gitRepoUrl = getGitRepoUrl(directory);
131
+ const projectIdentity = getProjectIdentity(projectRoot);
132
+ return {
133
+ tag: `${CONFIG.containerTagPrefix}_project_${sha256(projectIdentity)}`,
134
+ displayName: projectRoot,
135
+ projectPath: projectRoot,
136
+ projectName,
137
+ gitRepoUrl: gitRepoUrl || undefined,
138
+ };
139
+ }
140
+ export function getTags(directory) {
141
+ return {
142
+ user: getUserTagInfo(),
143
+ project: getProjectTagInfo(directory),
144
+ };
145
+ }
@@ -0,0 +1,3 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ export declare function performUserProfileLearning(ctx: PluginInput, directory: string): Promise<void>;
3
+ //# sourceMappingURL=user-memory-learning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-memory-learning.d.ts","sourceRoot":"","sources":["../../src/services/user-memory-learning.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAWvD,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAuEf"}