opencode-mem 2.3.7 → 2.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 (32) hide show
  1. package/README.md +15 -19
  2. package/dist/config.d.ts +4 -1
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +33 -39
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +40 -82
  7. package/dist/services/ai/validators/user-profile-validator.d.ts +0 -1
  8. package/dist/services/ai/validators/user-profile-validator.d.ts.map +1 -1
  9. package/dist/services/ai/validators/user-profile-validator.js +0 -17
  10. package/dist/services/api-handlers.d.ts +32 -3
  11. package/dist/services/api-handlers.d.ts.map +1 -1
  12. package/dist/services/api-handlers.js +89 -13
  13. package/dist/services/auto-capture.d.ts.map +1 -1
  14. package/dist/services/auto-capture.js +34 -34
  15. package/dist/services/language-detector.d.ts +3 -0
  16. package/dist/services/language-detector.d.ts.map +1 -0
  17. package/dist/services/language-detector.js +16 -0
  18. package/dist/services/secret-resolver.d.ts +2 -0
  19. package/dist/services/secret-resolver.d.ts.map +1 -0
  20. package/dist/services/secret-resolver.js +55 -0
  21. package/dist/services/user-memory-learning.d.ts.map +1 -1
  22. package/dist/services/user-memory-learning.js +27 -33
  23. package/dist/services/user-profile/types.d.ts +0 -5
  24. package/dist/services/user-profile/types.d.ts.map +1 -1
  25. package/dist/services/user-profile/user-profile-manager.d.ts.map +1 -1
  26. package/dist/services/user-profile/user-profile-manager.js +0 -7
  27. package/dist/services/user-prompt/user-prompt-manager.d.ts +2 -0
  28. package/dist/services/user-prompt/user-prompt-manager.d.ts.map +1 -1
  29. package/dist/services/user-prompt/user-prompt-manager.js +21 -0
  30. package/dist/web/app.js +191 -112
  31. package/dist/web/styles.css +202 -122
  32. package/package.json +3 -1
@@ -381,19 +381,22 @@ export async function handleSearch(query, tag, page = 1, pageSize = 20) {
381
381
  }
382
382
  await embeddingService.warmup();
383
383
  const queryVector = await embeddingService.embedWithTimeout(query);
384
- let allResults = [];
384
+ let memoryResults = [];
385
+ let promptResults = [];
385
386
  if (tag) {
386
387
  const { scope, hash } = extractScopeFromTag(tag);
387
388
  const shards = shardManager.getAllShards(scope, hash);
388
389
  for (const shard of shards) {
389
390
  try {
390
391
  const results = vectorSearch.searchInShard(shard, queryVector, tag, pageSize * 2);
391
- allResults.push(...results);
392
+ memoryResults.push(...results);
392
393
  }
393
394
  catch (error) {
394
395
  log("Shard search error", { shardId: shard.id, error: String(error) });
395
396
  }
396
397
  }
398
+ const projectPath = getProjectPathFromTag(tag);
399
+ promptResults = userPromptManager.searchPrompts(query, projectPath, pageSize * 2);
397
400
  }
398
401
  else {
399
402
  const projectShards = shardManager.getAllShards("project", "");
@@ -414,26 +417,33 @@ export async function handleSearch(query, tag, page = 1, pageSize = 20) {
414
417
  for (const shard of shards) {
415
418
  try {
416
419
  const results = vectorSearch.searchInShard(shard, queryVector, containerTag, pageSize);
417
- allResults.push(...results);
420
+ memoryResults.push(...results);
418
421
  }
419
422
  catch (error) {
420
423
  log("Shard search error", { shardId: shard.id, error: String(error) });
421
424
  }
422
425
  }
423
426
  }
424
- }
425
- const sortedResults = allResults.sort((a, b) => b.similarity - a.similarity);
426
- const total = sortedResults.length;
427
- const totalPages = Math.ceil(total / pageSize);
428
- const offset = (page - 1) * pageSize;
429
- const paginatedResults = sortedResults.slice(offset, offset + pageSize);
430
- const memories = paginatedResults.map((r) => ({
427
+ promptResults = userPromptManager.searchPrompts(query, undefined, pageSize * 2);
428
+ }
429
+ const formattedPrompts = promptResults.map((p) => ({
430
+ type: "prompt",
431
+ id: p.id,
432
+ sessionId: p.sessionId,
433
+ content: p.content,
434
+ createdAt: safeToISOString(p.createdAt),
435
+ projectPath: p.projectPath,
436
+ linkedMemoryId: p.linkedMemoryId,
437
+ similarity: 1.0,
438
+ }));
439
+ const formattedMemories = memoryResults.map((r) => ({
440
+ type: "memory",
431
441
  id: r.id,
432
442
  content: r.memory,
433
- type: r.metadata?.type,
443
+ memoryType: r.metadata?.type,
434
444
  createdAt: safeToISOString(r.metadata?.createdAt),
435
445
  updatedAt: r.metadata?.updatedAt ? safeToISOString(r.metadata.updatedAt) : undefined,
436
- similarity: Math.round(r.similarity * 100),
446
+ similarity: r.similarity,
437
447
  metadata: r.metadata,
438
448
  displayName: r.displayName,
439
449
  userName: r.userName,
@@ -442,11 +452,77 @@ export async function handleSearch(query, tag, page = 1, pageSize = 20) {
442
452
  projectName: r.projectName,
443
453
  gitRepoUrl: r.gitRepoUrl,
444
454
  isPinned: r.isPinned === 1,
455
+ linkedPromptId: r.metadata?.promptId,
445
456
  }));
457
+ const combinedResults = [...formattedMemories, ...formattedPrompts].sort((a, b) => (b.similarity || 0) - (a.similarity || 0) || b.createdAt.localeCompare(a.createdAt));
458
+ const total = combinedResults.length;
459
+ const totalPages = Math.ceil(total / pageSize);
460
+ const offset = (page - 1) * pageSize;
461
+ const paginatedResults = combinedResults.slice(offset, offset + pageSize);
462
+ const missingPromptIds = new Set();
463
+ const missingMemoryIds = new Set();
464
+ for (const item of paginatedResults) {
465
+ if (item.type === "memory" && item.linkedPromptId) {
466
+ const hasPrompt = paginatedResults.some((p) => p.id === item.linkedPromptId);
467
+ if (!hasPrompt)
468
+ missingPromptIds.add(item.linkedPromptId);
469
+ }
470
+ else if (item.type === "prompt" && item.linkedMemoryId) {
471
+ const hasMemory = paginatedResults.some((m) => m.id === item.linkedMemoryId);
472
+ if (!hasMemory)
473
+ missingMemoryIds.add(item.linkedMemoryId);
474
+ }
475
+ }
476
+ if (missingPromptIds.size > 0) {
477
+ const extraPrompts = userPromptManager.getPromptsByIds(Array.from(missingPromptIds));
478
+ for (const p of extraPrompts) {
479
+ paginatedResults.push({
480
+ type: "prompt",
481
+ id: p.id,
482
+ sessionId: p.sessionId,
483
+ content: p.content,
484
+ createdAt: safeToISOString(p.createdAt),
485
+ projectPath: p.projectPath,
486
+ linkedMemoryId: p.linkedMemoryId,
487
+ similarity: 0,
488
+ isContext: true,
489
+ });
490
+ }
491
+ }
492
+ if (missingMemoryIds.size > 0) {
493
+ const projectShards = shardManager.getAllShards("project", "");
494
+ for (const shard of projectShards) {
495
+ const db = connectionManager.getConnection(shard.dbPath);
496
+ for (const mid of missingMemoryIds) {
497
+ const m = vectorSearch.getMemoryById(db, mid);
498
+ if (m && !paginatedResults.some((existing) => existing.id === m.id)) {
499
+ paginatedResults.push({
500
+ type: "memory",
501
+ id: m.id,
502
+ content: m.content,
503
+ memoryType: m.type,
504
+ createdAt: safeToISOString(m.created_at),
505
+ updatedAt: m.updated_at ? safeToISOString(m.updated_at) : undefined,
506
+ similarity: 0,
507
+ metadata: safeJSONParse(m.metadata),
508
+ displayName: m.display_name,
509
+ userName: m.user_name,
510
+ userEmail: m.user_email,
511
+ projectPath: m.project_path,
512
+ projectName: m.project_name,
513
+ gitRepoUrl: m.git_repo_url,
514
+ isPinned: m.is_pinned === 1,
515
+ linkedPromptId: safeJSONParse(m.metadata)?.promptId,
516
+ isContext: true,
517
+ });
518
+ }
519
+ }
520
+ }
521
+ }
446
522
  return {
447
523
  success: true,
448
524
  data: {
449
- items: memories,
525
+ items: paginatedResults,
450
526
  total,
451
527
  page,
452
528
  pageSize,
@@ -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,27 +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
212
+ 5. You MUST write the summary in ${langName}.
203
213
 
204
214
  FORMAT:
205
215
  ## Request
206
- [1-2 sentences: what was requested]
216
+ [1-2 sentences: what was requested, in ${langName}]
207
217
 
208
218
  ## Outcome
209
- [1-2 sentences: what was done, include files/functions]
219
+ [1-2 sentences: what was done, include files/functions, in ${langName}]
210
220
 
211
221
  SKIP if: greetings, casual chat, no code/decisions made
212
- CAPTURE if: code changed, bug fixed, feature added, decision made
213
-
214
- EXAMPLES:
215
- Technical → type="feature":
216
- ## Request
217
- Fix function returning null.
218
- ## Outcome
219
- Changed searchMemories() to listMemories() in auto-capture.ts:166.
220
-
221
- Non-technical → type="skip", summary="":
222
- User greeted, AI introduced capabilities.`;
223
- const userPrompt = `${context}
222
+ CAPTURE if: code changed, bug fixed, feature added, decision made`;
223
+ const aiPrompt = `${context}
224
224
 
225
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.`;
226
226
  const toolSchema = {
@@ -244,7 +244,7 @@ Analyze this conversation. If it contains technical work (code, bugs, features,
244
244
  },
245
245
  },
246
246
  };
247
- const result = await provider.executeToolCall(systemPrompt, userPrompt, toolSchema, sessionID);
247
+ const result = await provider.executeToolCall(systemPrompt, aiPrompt, toolSchema, sessionID);
248
248
  if (!result.success || !result.data) {
249
249
  throw new Error(result.error || "Failed to generate summary");
250
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
+ }
@@ -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) {
@@ -112,10 +116,6 @@ Identify and ${existingProfile ? "update" : "create"}:
112
116
  - Development sequences, habits, learning style
113
117
  - Break down into steps if applicable
114
118
 
115
- 4. **Skill Level**
116
- - Overall: beginner/intermediate/advanced
117
- - Per-domain assessment (e.g., typescript: advanced, docker: beginner)
118
-
119
119
  ${existingProfile ? "Merge with existing profile, incrementing frequencies and updating confidence scores." : "Create initial profile with conservative confidence scores."}`;
120
120
  }
121
121
  async function analyzeUserProfile(context, existingProfile) {
@@ -140,6 +140,8 @@ async function analyzeUserProfile(context, existingProfile) {
140
140
 
141
141
  Your task is to analyze user prompts and ${existingProfile ? "update" : "create"} a comprehensive user profile.
142
142
 
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.
144
+
143
145
  Use the update_user_profile tool to save the ${existingProfile ? "updated" : "new"} profile.`;
144
146
  const toolSchema = {
145
147
  type: "function",
@@ -186,16 +188,8 @@ Use the update_user_profile tool to save the ${existingProfile ? "updated" : "ne
186
188
  required: ["description", "steps"],
187
189
  },
188
190
  },
189
- skillLevel: {
190
- type: "object",
191
- properties: {
192
- overall: { type: "string", enum: ["beginner", "intermediate", "advanced"] },
193
- domains: { type: "object", additionalProperties: { type: "string" } },
194
- },
195
- required: ["overall", "domains"],
196
- },
197
191
  },
198
- required: ["preferences", "patterns", "workflows", "skillLevel"],
192
+ required: ["preferences", "patterns", "workflows"],
199
193
  },
200
194
  },
201
195
  };
@@ -16,15 +16,10 @@ export interface UserProfileWorkflow {
16
16
  steps: string[];
17
17
  frequency: number;
18
18
  }
19
- export interface UserProfileSkillLevel {
20
- overall: string;
21
- domains: Record<string, string>;
22
- }
23
19
  export interface UserProfileData {
24
20
  preferences: UserProfilePreference[];
25
21
  patterns: UserProfilePattern[];
26
22
  workflows: UserProfileWorkflow[];
27
- skillLevel: UserProfileSkillLevel;
28
23
  }
29
24
  export interface UserProfile {
30
25
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,qBAAqB,EAAE,CAAC;IACrC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,UAAU,EAAE,qBAAqB,CAAC;CACnC;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,qBAAqB,EAAE,CAAC;IACrC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,SAAS,EAAE,mBAAmB,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"user-profile-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/user-profile-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKrF,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IA0CpB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAapD,aAAa,CACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,eAAe,EAAE,MAAM,GACtB,MAAM;IA8BT,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,yBAAyB,EAAE,MAAM,EACjC,aAAa,EAAE,MAAM,GACpB,IAAI;IA6BP,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,oBAAoB;IAiB5B,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,oBAAoB,EAAE;IAYnF,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IA2B7C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAOrD,oBAAoB,IAAI,WAAW,EAAE;IAMrC,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,cAAc;IAYtB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe;CA2FhG;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
1
+ {"version":3,"file":"user-profile-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/user-profile-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKrF,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IA0CpB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAapD,aAAa,CACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,eAAe,EAAE,MAAM,GACtB,MAAM;IA8BT,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,yBAAyB,EAAE,MAAM,EACjC,aAAa,EAAE,MAAM,GACpB,IAAI;IA6BP,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,oBAAoB;IAiB5B,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,oBAAoB,EAAE;IAYnF,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IA2B7C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAOrD,oBAAoB,IAAI,WAAW,EAAE;IAMrC,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,cAAc;IAYtB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe;CAmFhG;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
@@ -194,7 +194,6 @@ export class UserProfileManager {
194
194
  preferences: safeArray(existing?.preferences),
195
195
  patterns: safeArray(existing?.patterns),
196
196
  workflows: safeArray(existing?.workflows),
197
- skillLevel: safeObject(existing?.skillLevel, { overall: "intermediate", domains: {} }),
198
197
  };
199
198
  if (updates.preferences) {
200
199
  for (const newPref of updates.preferences) {
@@ -258,12 +257,6 @@ export class UserProfileManager {
258
257
  merged.workflows.sort((a, b) => b.frequency - a.frequency);
259
258
  merged.workflows = merged.workflows.slice(0, CONFIG.userProfileMaxWorkflows);
260
259
  }
261
- if (updates.skillLevel) {
262
- merged.skillLevel = {
263
- overall: updates.skillLevel.overall || merged.skillLevel.overall,
264
- domains: { ...merged.skillLevel.domains, ...safeObject(updates.skillLevel.domains, {}) },
265
- };
266
- }
267
260
  return merged;
268
261
  }
269
262
  }
@@ -32,6 +32,8 @@ export declare class UserPromptManager {
32
32
  linkMemoryToPrompt(promptId: string, memoryId: string): void;
33
33
  getPromptById(promptId: string): UserPrompt | null;
34
34
  getCapturedPrompts(projectPath?: string): UserPrompt[];
35
+ searchPrompts(query: string, projectPath?: string, limit?: number): UserPrompt[];
36
+ getPromptsByIds(ids: string[]): UserPrompt[];
35
37
  private rowToPrompt;
36
38
  }
37
39
  export declare const userPromptManager: UserPromptManager;
@@ -1 +1 @@
1
- {"version":3,"file":"user-prompt-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-prompt/user-prompt-manager.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IA+BpB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAa9F,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAc7D,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKtC,sBAAsB,IAAI,MAAM;IAMhC,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAUjD,8BAA8B,IAAI,MAAM;IAQxC,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYtD,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKlD,kCAAkC,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAU7D,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE;IAiBpF,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK5D,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAOlD,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAgBtD,OAAO,CAAC,WAAW;CAapB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
1
+ {"version":3,"file":"user-prompt-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-prompt/user-prompt-manager.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IA+BpB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAa9F,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAc7D,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKtC,sBAAsB,IAAI,MAAM;IAMhC,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAUjD,8BAA8B,IAAI,MAAM;IAQxC,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAYtD,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKlD,kCAAkC,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAU7D,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE;IAiBpF,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK5D,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAOlD,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAgBtD,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IAiBpF,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE;IAQ5C,OAAO,CAAC,WAAW;CAapB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
@@ -147,6 +147,27 @@ export class UserPromptManager {
147
147
  const rows = stmt.all(...params);
148
148
  return rows.map((row) => this.rowToPrompt(row));
149
149
  }
150
+ searchPrompts(query, projectPath, limit = 20) {
151
+ let sql = `SELECT * FROM user_prompts WHERE content LIKE ? AND captured = 1`;
152
+ const params = [`%${query}%`];
153
+ if (projectPath) {
154
+ sql += ` AND project_path = ?`;
155
+ params.push(projectPath);
156
+ }
157
+ sql += ` ORDER BY created_at DESC LIMIT ?`;
158
+ params.push(limit);
159
+ const stmt = this.db.prepare(sql);
160
+ const rows = stmt.all(...params);
161
+ return rows.map((row) => this.rowToPrompt(row));
162
+ }
163
+ getPromptsByIds(ids) {
164
+ if (ids.length === 0)
165
+ return [];
166
+ const placeholders = ids.map(() => "?").join(",");
167
+ const stmt = this.db.prepare(`SELECT * FROM user_prompts WHERE id IN (${placeholders})`);
168
+ const rows = stmt.all(...ids);
169
+ return rows.map((row) => this.rowToPrompt(row));
170
+ }
150
171
  rowToPrompt(row) {
151
172
  return {
152
173
  id: row.id,