opencode-mem 2.4.0 → 2.5.1

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.
package/README.md CHANGED
@@ -52,38 +52,36 @@ Configure at `~/.config/opencode/opencode-mem.jsonc`:
52
52
  ```jsonc
53
53
  {
54
54
  "storagePath": "~/.opencode-mem/data",
55
+ "userEmailOverride": "user@example.com",
56
+ "userNameOverride": "John Doe",
55
57
  "embeddingModel": "Xenova/nomic-embed-text-v1",
56
58
  "webServerEnabled": true,
57
59
  "webServerPort": 4747,
60
+
58
61
  "autoCaptureEnabled": true,
62
+ "autoCaptureLanguage": "auto",
59
63
  "memoryProvider": "openai-chat",
60
- "memoryModel": "gpt-4",
64
+ "memoryModel": "gpt-4o-mini",
65
+ "memoryApiUrl": "https://api.openai.com/v1",
66
+ "memoryApiKey": "sk-...",
67
+
68
+ "showAutoCaptureToasts": true,
69
+ "showUserProfileToasts": true,
70
+ "showErrorToasts": true,
71
+
61
72
  "userProfileAnalysisInterval": 10,
62
73
  "maxMemories": 10
63
74
  }
64
75
  ```
65
76
 
66
- Full documentation available in our [Configuration Guide](https://github.com/tickernelz/opencode-mem/wiki/Configuration-Guide).
67
-
68
- ## Important: v2.3 Breaking Changes
69
-
70
- User-scoped memories removed. All memories now project-scoped. Update configuration:
71
-
77
+ **API Key Formats:**
72
78
  ```jsonc
73
- // OLD: Remove these
74
- {
75
- "userMemoryAnalysisInterval": 10,
76
- "maxProjectMemories": 10
77
- }
78
-
79
- // NEW: Use only
80
- {
81
- "userProfileAnalysisInterval": 10,
82
- "maxMemories": 10
83
- }
79
+ "memoryApiKey": "sk-..."
80
+ "memoryApiKey": "file://~/.config/opencode/api-key.txt"
81
+ "memoryApiKey": "env://OPENAI_API_KEY"
84
82
  ```
85
83
 
86
- Remove `scope` parameter from all `memory()` calls. See [Migration Guide](https://github.com/tickernelz/opencode-mem/wiki/Migration-v2-3) for details.
84
+ Full documentation available in our [Configuration Guide](https://github.com/tickernelz/opencode-mem/wiki/Configuration-Guide).
87
85
 
88
86
  ## Documentation
89
87
 
package/dist/config.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export declare const CONFIG: {
2
2
  storagePath: string;
3
3
  customSqlitePath: string | undefined;
4
+ userEmailOverride: string | undefined;
5
+ userNameOverride: string | undefined;
4
6
  embeddingModel: string;
5
7
  embeddingDimensions: number;
6
8
  embeddingApiUrl: string | undefined;
@@ -13,6 +15,7 @@ export declare const CONFIG: {
13
15
  autoCaptureEnabled: boolean;
14
16
  autoCaptureMaxIterations: number;
15
17
  autoCaptureIterationTimeout: number;
18
+ autoCaptureLanguage: string | undefined;
16
19
  memoryProvider: "openai-chat" | "openai-responses" | "anthropic";
17
20
  memoryModel: string | undefined;
18
21
  memoryApiUrl: string | undefined;
@@ -32,6 +35,9 @@ export declare const CONFIG: {
32
35
  userProfileMaxWorkflows: number;
33
36
  userProfileConfidenceDecayDays: number;
34
37
  userProfileChangelogRetentionCount: number;
38
+ showAutoCaptureToasts: boolean;
39
+ showUserProfileToasts: boolean;
40
+ showErrorToasts: boolean;
35
41
  };
36
42
  export declare function isConfigured(): boolean;
37
43
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAuVA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;oBAwBb,aAAa,GACb,kBAAkB,GAClB,WAAW;;;;;;;;;;;;;;;;;;;CAyBhB,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAgYA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;oBA2Bb,aAAa,GACb,kBAAkB,GAClB,WAAW;;;;;;;;;;;;;;;;;;;;;;CA4BhB,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
package/dist/config.js CHANGED
@@ -2,6 +2,7 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { stripJsoncComments } from "./services/jsonc.js";
5
+ import { resolveSecretValue } from "./services/secret-resolver.js";
5
6
  const CONFIG_DIR = join(homedir(), ".config", "opencode");
6
7
  const DATA_DIR = join(homedir(), ".opencode-mem");
7
8
  const CONFIG_FILES = [
@@ -41,6 +42,9 @@ const DEFAULTS = {
41
42
  userProfileMaxWorkflows: 10,
42
43
  userProfileConfidenceDecayDays: 30,
43
44
  userProfileChangelogRetentionCount: 5,
45
+ showAutoCaptureToasts: true,
46
+ showUserProfileToasts: true,
47
+ showErrorToasts: true,
44
48
  };
45
49
  function expandPath(path) {
46
50
  if (path.startsWith("~/")) {
@@ -72,6 +76,9 @@ const CONFIG_TEMPLATE = `{
72
76
 
73
77
  // Storage location for vector database
74
78
  "storagePath": "~/.opencode-mem/data",
79
+
80
+ "userEmailOverride": "",
81
+ "userNameOverride": "",
75
82
 
76
83
  // ============================================
77
84
  // macOS SQLite Extension Loading (REQUIRED FOR macOS)
@@ -158,6 +165,11 @@ const CONFIG_TEMPLATE = `{
158
165
  "memoryModel": "gpt-4o-mini",
159
166
  "memoryApiUrl": "https://api.openai.com/v1",
160
167
  "memoryApiKey": "sk-...",
168
+
169
+ // API Key Formats:
170
+ // Direct value: "sk-..."
171
+ // From file: "file://~/.config/litellm-key.txt"
172
+ // From env variable: "env://LITELLM_API_KEY"
161
173
 
162
174
  // Examples for different providers:
163
175
  // OpenAI Chat Completion (default, backward compatible):
@@ -193,6 +205,23 @@ const CONFIG_TEMPLATE = `{
193
205
  // Days to keep AI session history before cleanup
194
206
  "aiSessionRetentionDays": 7,
195
207
 
208
+ // Language for auto-capture summaries (default: "auto" for auto-detection)
209
+ // Options: "auto", "en", "id", "zh", "ja", "es", "fr", "de", "ru", "pt", "ar", "ko"
210
+ // "autoCaptureLanguage": "auto",
211
+
212
+ // ============================================
213
+ // Toast Notifications
214
+ // ============================================
215
+
216
+ // Show toast when memory is auto-captured
217
+ "showAutoCaptureToasts": true,
218
+
219
+ // Show toast when user profile is updated
220
+ "showUserProfileToasts": true,
221
+
222
+ // Show toast for error messages
223
+ "showErrorToasts": true,
224
+
196
225
  // ============================================
197
226
  // User Profile System
198
227
  // ============================================
@@ -280,12 +309,14 @@ export const CONFIG = {
280
309
  customSqlitePath: fileConfig.customSqlitePath
281
310
  ? expandPath(fileConfig.customSqlitePath)
282
311
  : undefined,
312
+ userEmailOverride: fileConfig.userEmailOverride,
313
+ userNameOverride: fileConfig.userNameOverride,
283
314
  embeddingModel: fileConfig.embeddingModel ?? DEFAULTS.embeddingModel,
284
315
  embeddingDimensions: fileConfig.embeddingDimensions ??
285
316
  getEmbeddingDimensions(fileConfig.embeddingModel ?? DEFAULTS.embeddingModel),
286
317
  embeddingApiUrl: fileConfig.embeddingApiUrl,
287
318
  embeddingApiKey: fileConfig.embeddingApiUrl
288
- ? (fileConfig.embeddingApiKey ?? process.env.OPENAI_API_KEY)
319
+ ? resolveSecretValue(fileConfig.embeddingApiKey ?? process.env.OPENAI_API_KEY)
289
320
  : undefined,
290
321
  similarityThreshold: fileConfig.similarityThreshold ?? DEFAULTS.similarityThreshold,
291
322
  maxMemories: fileConfig.maxMemories ?? DEFAULTS.maxMemories,
@@ -295,10 +326,11 @@ export const CONFIG = {
295
326
  autoCaptureEnabled: fileConfig.autoCaptureEnabled ?? DEFAULTS.autoCaptureEnabled,
296
327
  autoCaptureMaxIterations: fileConfig.autoCaptureMaxIterations ?? DEFAULTS.autoCaptureMaxIterations,
297
328
  autoCaptureIterationTimeout: fileConfig.autoCaptureIterationTimeout ?? DEFAULTS.autoCaptureIterationTimeout,
329
+ autoCaptureLanguage: fileConfig.autoCaptureLanguage,
298
330
  memoryProvider: (fileConfig.memoryProvider ?? "openai-chat"),
299
331
  memoryModel: fileConfig.memoryModel,
300
332
  memoryApiUrl: fileConfig.memoryApiUrl,
301
- memoryApiKey: fileConfig.memoryApiKey,
333
+ memoryApiKey: resolveSecretValue(fileConfig.memoryApiKey),
302
334
  aiSessionRetentionDays: fileConfig.aiSessionRetentionDays ?? DEFAULTS.aiSessionRetentionDays,
303
335
  webServerEnabled: fileConfig.webServerEnabled ?? DEFAULTS.webServerEnabled,
304
336
  webServerPort: fileConfig.webServerPort ?? DEFAULTS.webServerPort,
@@ -314,6 +346,9 @@ export const CONFIG = {
314
346
  userProfileMaxWorkflows: fileConfig.userProfileMaxWorkflows ?? DEFAULTS.userProfileMaxWorkflows,
315
347
  userProfileConfidenceDecayDays: fileConfig.userProfileConfidenceDecayDays ?? DEFAULTS.userProfileConfidenceDecayDays,
316
348
  userProfileChangelogRetentionCount: fileConfig.userProfileChangelogRetentionCount ?? DEFAULTS.userProfileChangelogRetentionCount,
349
+ showAutoCaptureToasts: fileConfig.showAutoCaptureToasts ?? DEFAULTS.showAutoCaptureToasts,
350
+ showUserProfileToasts: fileConfig.showUserProfileToasts ?? DEFAULTS.showUserProfileToasts,
351
+ showErrorToasts: fileConfig.showErrorToasts ?? DEFAULTS.showErrorToasts,
317
352
  };
318
353
  export function isConfigured() {
319
354
  return true;
package/dist/index.js CHANGED
@@ -148,7 +148,7 @@ export const OpenCodeMemPlugin = async (ctx) => {
148
148
  }
149
149
  catch (error) {
150
150
  log("chat.message: ERROR", { error: String(error) });
151
- if (ctx.client?.tui) {
151
+ if (ctx.client?.tui && CONFIG.showErrorToasts) {
152
152
  await ctx.client.tui
153
153
  .showToast({
154
154
  body: {
@@ -392,7 +392,7 @@ export const OpenCodeMemPlugin = async (ctx) => {
392
392
  })
393
393
  .catch((err) => {
394
394
  log("Auto-cleanup failed", { error: String(err) });
395
- if (ctx.client?.tui) {
395
+ if (ctx.client?.tui && CONFIG.showErrorToasts) {
396
396
  ctx.client.tui
397
397
  .showToast({
398
398
  body: {
@@ -1 +1 @@
1
- {"version":3,"file":"auto-capture.d.ts","sourceRoot":"","sources":["../../src/services/auto-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAcvD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAgGf"}
1
+ {"version":3,"file":"auto-capture.d.ts","sourceRoot":"","sources":["../../src/services/auto-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAcvD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAoGf"}
@@ -37,7 +37,7 @@ export async function performAutoCapture(ctx, sessionID, directory) {
37
37
  const tags = getTags(directory);
38
38
  const latestMemory = await getLatestProjectMemory(tags.project.tag);
39
39
  const context = buildMarkdownContext(prompt.content, textResponses, toolCalls, latestMemory);
40
- const summaryResult = await generateSummary(context, sessionID);
40
+ const summaryResult = await generateSummary(context, sessionID, prompt.content);
41
41
  if (!summaryResult || summaryResult.type === "skip") {
42
42
  log("Auto-capture: skipped non-technical conversation", { sessionID });
43
43
  userPromptManager.deletePrompt(prompt.id);
@@ -59,31 +59,35 @@ export async function performAutoCapture(ctx, sessionID, directory) {
59
59
  if (result.success) {
60
60
  userPromptManager.linkMemoryToPrompt(prompt.id, result.id);
61
61
  userPromptManager.markAsCaptured(prompt.id);
62
+ if (CONFIG.showAutoCaptureToasts) {
63
+ await ctx.client?.tui
64
+ .showToast({
65
+ body: {
66
+ title: "Memory Captured",
67
+ message: "Project memory saved from conversation",
68
+ variant: "success",
69
+ duration: 3000,
70
+ },
71
+ })
72
+ .catch(() => { });
73
+ }
74
+ }
75
+ }
76
+ catch (error) {
77
+ log("Auto-capture error", { sessionID, error: String(error) });
78
+ if (CONFIG.showErrorToasts) {
62
79
  await ctx.client?.tui
63
80
  .showToast({
64
81
  body: {
65
- title: "Memory Captured",
66
- message: "Project memory saved from conversation",
67
- variant: "success",
68
- duration: 3000,
82
+ title: "Auto-Capture Failed",
83
+ message: String(error),
84
+ variant: "error",
85
+ duration: 5000,
69
86
  },
70
87
  })
71
88
  .catch(() => { });
72
89
  }
73
90
  }
74
- catch (error) {
75
- log("Auto-capture error", { sessionID, error: String(error) });
76
- await ctx.client?.tui
77
- .showToast({
78
- body: {
79
- title: "Auto-Capture Failed",
80
- message: String(error),
81
- variant: "error",
82
- duration: 5000,
83
- },
84
- })
85
- .catch(() => { });
86
- }
87
91
  }
88
92
  function extractAIContent(messages) {
89
93
  const textResponses = [];
@@ -180,11 +184,12 @@ function buildMarkdownContext(userPrompt, textResponses, toolCalls, latestMemory
180
184
  }
181
185
  return sections.join("\n");
182
186
  }
183
- async function generateSummary(context, sessionID) {
187
+ async function generateSummary(context, sessionID, userPrompt) {
184
188
  if (!CONFIG.memoryModel || !CONFIG.memoryApiUrl) {
185
189
  throw new Error("External API not configured for auto-capture");
186
190
  }
187
191
  const { AIProviderFactory } = await import("./ai/ai-provider-factory.js");
192
+ const { detectLanguage, getLanguageName } = await import("./language-detector.js");
188
193
  const providerConfig = {
189
194
  model: CONFIG.memoryModel,
190
195
  apiUrl: CONFIG.memoryApiUrl,
@@ -193,6 +198,10 @@ async function generateSummary(context, sessionID) {
193
198
  iterationTimeout: CONFIG.autoCaptureIterationTimeout,
194
199
  };
195
200
  const provider = AIProviderFactory.createProvider(CONFIG.memoryProvider, providerConfig);
201
+ const targetLang = CONFIG.autoCaptureLanguage === "auto" || !CONFIG.autoCaptureLanguage
202
+ ? detectLanguage(userPrompt)
203
+ : CONFIG.autoCaptureLanguage;
204
+ const langName = getLanguageName(targetLang);
196
205
  const systemPrompt = `You are a technical memory recorder for a software development project.
197
206
 
198
207
  RULES:
@@ -200,34 +209,18 @@ RULES:
200
209
  2. SKIP non-technical by returning type="skip"
201
210
  3. NO meta-commentary or behavior analysis
202
211
  4. Include specific file names, functions, technical details
203
- 5. DETECT the language used by the user. You MUST write the summary and reasoning in that SAME language.
212
+ 5. You MUST write the summary in ${langName}.
204
213
 
205
214
  FORMAT:
206
215
  ## Request
207
- [1-2 sentences: what was requested, in user's language]
216
+ [1-2 sentences: what was requested, in ${langName}]
208
217
 
209
218
  ## Outcome
210
- [1-2 sentences: what was done, include files/functions, in user's language]
219
+ [1-2 sentences: what was done, include files/functions, in ${langName}]
211
220
 
212
221
  SKIP if: greetings, casual chat, no code/decisions made
213
- CAPTURE if: code changed, bug fixed, feature added, decision made
214
-
215
- EXAMPLES:
216
- Technical (English) → type="feature":
217
- ## Request
218
- Fix function returning null.
219
- ## Outcome
220
- Changed searchMemories() to listMemories() in auto-capture.ts:166.
221
-
222
- Technical (Indonesian) → type="feature":
223
- ## Request
224
- Perbaiki fungsi yang mengembalikan null.
225
- ## Outcome
226
- Mengubah searchMemories() menjadi listMemories() di auto-capture.ts:166.
227
-
228
- Non-technical → type="skip", summary="":
229
- User greeted, AI introduced capabilities.`;
230
- const userPrompt = `${context}
222
+ CAPTURE if: code changed, bug fixed, feature added, decision made`;
223
+ const aiPrompt = `${context}
231
224
 
232
225
  Analyze this conversation. If it contains technical work (code, bugs, features, decisions), create a concise summary. If it's non-technical (greetings, casual chat, incomplete requests), return type="skip" with empty summary.`;
233
226
  const toolSchema = {
@@ -251,7 +244,7 @@ Analyze this conversation. If it contains technical work (code, bugs, features,
251
244
  },
252
245
  },
253
246
  };
254
- const result = await provider.executeToolCall(systemPrompt, userPrompt, toolSchema, sessionID);
247
+ const result = await provider.executeToolCall(systemPrompt, aiPrompt, toolSchema, sessionID);
255
248
  if (!result.success || !result.data) {
256
249
  throw new Error(result.error || "Failed to generate summary");
257
250
  }
@@ -0,0 +1,3 @@
1
+ export declare function detectLanguage(text: string): string;
2
+ export declare function getLanguageName(code: string): string;
3
+ //# sourceMappingURL=language-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-detector.d.ts","sourceRoot":"","sources":["../../src/services/language-detector.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYnD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGpD"}
@@ -0,0 +1,16 @@
1
+ import { franc } from "franc-min";
2
+ import { iso6393, iso6393To1 } from "iso-639-3";
3
+ export function detectLanguage(text) {
4
+ if (!text || text.trim().length === 0) {
5
+ return "en";
6
+ }
7
+ const detected = franc(text, { minLength: 10 });
8
+ if (detected === "und") {
9
+ return "en";
10
+ }
11
+ return iso6393To1[detected] || "en";
12
+ }
13
+ export function getLanguageName(code) {
14
+ const lang = iso6393.find((l) => l.iso6391 === code);
15
+ return lang?.name || "English";
16
+ }
@@ -0,0 +1,2 @@
1
+ export declare function resolveSecretValue(value: string | undefined): string | undefined;
2
+ //# sourceMappingURL=secret-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-resolver.d.ts","sourceRoot":"","sources":["../../src/services/secret-resolver.ts"],"names":[],"mappings":"AAiCA,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAkChF"}
@@ -0,0 +1,55 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir, platform } from "node:os";
4
+ function expandPath(path) {
5
+ if (path.startsWith("~/")) {
6
+ return join(homedir(), path.slice(2));
7
+ }
8
+ if (path === "~") {
9
+ return homedir();
10
+ }
11
+ return path;
12
+ }
13
+ function checkFilePermissions(filePath) {
14
+ if (platform() === "win32") {
15
+ return;
16
+ }
17
+ try {
18
+ const stats = statSync(filePath);
19
+ const mode = stats.mode & 0o777;
20
+ if (mode > 0o600) {
21
+ console.warn(`Warning: Secret file ${filePath} has permissive permissions (${mode.toString(8)}). Recommend chmod 600.`);
22
+ }
23
+ }
24
+ catch (error) {
25
+ console.warn(`Warning: Could not check file permissions for ${filePath}`);
26
+ }
27
+ }
28
+ export function resolveSecretValue(value) {
29
+ if (!value) {
30
+ return undefined;
31
+ }
32
+ if (value.startsWith("file://")) {
33
+ const filePath = expandPath(value.slice(7));
34
+ if (!existsSync(filePath)) {
35
+ throw new Error(`Secret file not found: ${filePath}`);
36
+ }
37
+ try {
38
+ checkFilePermissions(filePath);
39
+ const content = readFileSync(filePath, "utf-8");
40
+ return content.trim();
41
+ }
42
+ catch (error) {
43
+ throw new Error(`Failed to read secret file ${filePath}: ${error}`);
44
+ }
45
+ }
46
+ if (value.startsWith("env://")) {
47
+ const envVar = value.slice(6);
48
+ const envValue = process.env[envVar];
49
+ if (!envValue) {
50
+ throw new Error(`Environment variable not found: ${envVar}`);
51
+ }
52
+ return envValue;
53
+ }
54
+ return value;
55
+ }
@@ -42,8 +42,8 @@ export function getProjectName(directory) {
42
42
  return parts[parts.length - 1] || directory;
43
43
  }
44
44
  export function getUserTagInfo() {
45
- const email = getGitEmail();
46
- const name = getGitName();
45
+ const email = CONFIG.userEmailOverride || getGitEmail();
46
+ const name = CONFIG.userNameOverride || getGitName();
47
47
  if (email) {
48
48
  return {
49
49
  tag: `${CONFIG.containerTagPrefix}_user_${sha256(email)}`,
@@ -52,7 +52,7 @@ export function getUserTagInfo() {
52
52
  userEmail: email,
53
53
  };
54
54
  }
55
- const fallback = process.env.USER || process.env.USERNAME || "anonymous";
55
+ const fallback = name || process.env.USER || process.env.USERNAME || "anonymous";
56
56
  return {
57
57
  tag: `${CONFIG.containerTagPrefix}_user_${sha256(fallback)}`,
58
58
  displayName: fallback,
@@ -1 +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;AASvD,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAmFf"}
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;AASvD,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAuFf"}
@@ -32,16 +32,18 @@ export async function performUserProfileLearning(ctx, directory) {
32
32
  userProfileManager.createProfile(userId, tags.user.displayName || "Unknown", tags.user.userName || "unknown", tags.user.userEmail || "unknown", updatedProfileData, prompts.length);
33
33
  }
34
34
  userPromptManager.markMultipleAsUserLearningCaptured(prompts.map((p) => p.id));
35
- await ctx.client?.tui
36
- .showToast({
37
- body: {
38
- title: "User Profile Updated",
39
- message: `Analyzed ${prompts.length} prompts and updated your profile`,
40
- variant: "success",
41
- duration: 3000,
42
- },
43
- })
44
- .catch(() => { });
35
+ if (CONFIG.showUserProfileToasts) {
36
+ await ctx.client?.tui
37
+ .showToast({
38
+ body: {
39
+ title: "User Profile Updated",
40
+ message: `Analyzed ${prompts.length} prompts and updated your profile`,
41
+ variant: "success",
42
+ duration: 3000,
43
+ },
44
+ })
45
+ .catch(() => { });
46
+ }
45
47
  }
46
48
  catch (error) {
47
49
  const errorStack = error instanceof Error ? error.stack : undefined;
@@ -50,16 +52,18 @@ export async function performUserProfileLearning(ctx, directory) {
50
52
  stack: errorStack,
51
53
  errorType: error instanceof Error ? error.constructor.name : typeof error,
52
54
  });
53
- await ctx.client?.tui
54
- .showToast({
55
- body: {
56
- title: "User Profile Update Failed",
57
- message: String(error),
58
- variant: "error",
59
- duration: 5000,
60
- },
61
- })
62
- .catch(() => { });
55
+ if (CONFIG.showErrorToasts) {
56
+ await ctx.client?.tui
57
+ .showToast({
58
+ body: {
59
+ title: "User Profile Update Failed",
60
+ message: String(error),
61
+ variant: "error",
62
+ duration: 5000,
63
+ },
64
+ })
65
+ .catch(() => { });
66
+ }
63
67
  }
64
68
  }
65
69
  function generateChangeSummary(oldProfile, newProfile) {
@@ -136,7 +140,7 @@ async function analyzeUserProfile(context, existingProfile) {
136
140
 
137
141
  Your task is to analyze user prompts and ${existingProfile ? "update" : "create"} a comprehensive user profile.
138
142
 
139
- CRITICAL: Detect the language used by the user. You MUST output all descriptions and text in the SAME language as the user's prompts.
143
+ CRITICAL: Detect the language used by the user in their prompts. You MUST output all descriptions, categories, and text in the SAME language as the user's prompts.
140
144
 
141
145
  Use the update_user_profile tool to save the ${existingProfile ? "updated" : "new"} profile.`;
142
146
  const toolSchema = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-mem",
3
- "version": "2.4.0",
3
+ "version": "2.5.1",
4
4
  "description": "OpenCode plugin that gives coding agents persistent memory using local vector database",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
@@ -34,6 +34,8 @@
34
34
  "dependencies": {
35
35
  "@opencode-ai/plugin": "^1.0.162",
36
36
  "@xenova/transformers": "^2.17.2",
37
+ "franc-min": "^6.2.0",
38
+ "iso-639-3": "^3.0.1",
37
39
  "sqlite-vec": "^0.1.7-alpha.2"
38
40
  },
39
41
  "devDependencies": {