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
package/dist/web/app.js CHANGED
@@ -115,12 +115,14 @@ function renderMemories() {
115
115
  return;
116
116
  }
117
117
 
118
- container.innerHTML = state.memories
119
- .map((item) => {
120
- if (item.type === "prompt") {
121
- return renderPromptCard(item);
118
+ container.innerHTML = groupMemories(state.memories)
119
+ .map((group) => {
120
+ if (group.isPair) {
121
+ return renderCombinedCard(group);
122
+ } else if (group.type === "prompt") {
123
+ return renderPromptCard(group.item);
122
124
  } else {
123
- return renderMemoryCard(item);
125
+ return renderMemoryCard(group.item);
124
126
  }
125
127
  })
126
128
  .join("");
@@ -132,6 +134,82 @@ function renderMemories() {
132
134
  lucide.createIcons();
133
135
  }
134
136
 
137
+ function groupMemories(items) {
138
+ const map = new Map();
139
+ const pairs = [];
140
+ const processed = new Set();
141
+
142
+ items.forEach((item) => map.set(item.id, item));
143
+
144
+ items.forEach((item) => {
145
+ if (processed.has(item.id)) return;
146
+
147
+ if (item.type === "memory" && item.linkedPromptId && map.has(item.linkedPromptId)) {
148
+ const prompt = map.get(item.linkedPromptId);
149
+ pairs.push({ isPair: true, memory: item, prompt: prompt });
150
+ processed.add(item.id);
151
+ processed.add(prompt.id);
152
+ } else if (item.type === "prompt" && item.linkedMemoryId && map.has(item.linkedMemoryId)) {
153
+ const memory = map.get(item.linkedMemoryId);
154
+ pairs.push({ isPair: true, memory: memory, prompt: item });
155
+ processed.add(item.id);
156
+ processed.add(memory.id);
157
+ } else {
158
+ pairs.push({ isPair: false, type: item.type, item: item });
159
+ processed.add(item.id);
160
+ }
161
+ });
162
+
163
+ return pairs.sort((a, b) => {
164
+ const timeA = a.isPair ? a.memory.createdAt : a.item.createdAt;
165
+ const timeB = b.isPair ? b.memory.createdAt : b.item.createdAt;
166
+ return new Date(timeB) - new Date(timeA);
167
+ });
168
+ }
169
+
170
+ function renderCombinedCard(pair) {
171
+ const { memory, prompt } = pair;
172
+ const isSelected = state.selectedMemories.has(memory.id);
173
+ const similarityHtml =
174
+ memory.similarity !== undefined
175
+ ? `<span class="similarity-score">${Math.round(memory.similarity * 100)}%</span>`
176
+ : "";
177
+
178
+ return `
179
+ <div class="combined-card ${isSelected ? "selected" : ""}" data-id="${memory.id}">
180
+ <div class="combined-prompt-section">
181
+ <div class="combined-header">
182
+ <span class="badge badge-prompt">USER PROMPT</span>
183
+ <span class="prompt-date">${formatDate(prompt.createdAt)}</span>
184
+ </div>
185
+ <div class="prompt-content">${escapeHtml(prompt.content)}</div>
186
+ </div>
187
+
188
+ <div class="combined-divider">
189
+ <i data-lucide="arrow-down" class="divider-icon"></i>
190
+ </div>
191
+
192
+ <div class="combined-memory-section">
193
+ <div class="memory-header">
194
+ <div class="meta">
195
+ <input type="checkbox" class="memory-checkbox" data-id="${memory.id}" ${isSelected ? "checked" : ""} />
196
+ <span class="badge badge-memory">MEMORY</span>
197
+ ${memory.memoryType ? `<span class="badge badge-type">${memory.memoryType}</span>` : ""}
198
+ ${similarityHtml}
199
+ <span class="memory-display-name">${escapeHtml(memory.displayName || memory.id)}</span>
200
+ </div>
201
+ <div class="memory-actions">
202
+ <button class="btn-delete" onclick="deleteMemoryWithLink('${memory.id}', true)">
203
+ <i data-lucide="trash-2" class="icon"></i> Delete Pair
204
+ </button>
205
+ </div>
206
+ </div>
207
+ <div class="memory-content markdown-content">${renderMarkdown(memory.content)}</div>
208
+ </div>
209
+ </div>
210
+ `;
211
+ }
212
+
135
213
  function renderPromptCard(prompt) {
136
214
  const isLinked = !!prompt.linkedMemoryId;
137
215
  const isSelected = state.selectedMemories.has(prompt.id);
@@ -723,129 +801,130 @@ function renderUserProfile() {
723
801
  const preferences = data.preferences || [];
724
802
  const patterns = data.patterns || [];
725
803
  const workflows = data.workflows || [];
726
- const skillLevel = data.skillLevel || {};
727
804
 
728
805
  container.innerHTML = `
729
806
  <div class="profile-header">
730
807
  <div class="profile-info">
731
808
  <h3>${profile.displayName || profile.userId}</h3>
732
- <p class="profile-meta">
733
- <span>Version: ${profile.version}</span>
734
- <span class="separator">|</span>
735
- <span>Analyzed: ${profile.totalPromptsAnalyzed} prompts</span>
736
- <span class="separator">|</span>
737
- <span>Updated: ${formatDate(profile.lastAnalyzedAt)}</span>
738
- </p>
809
+ <div class="profile-stats">
810
+ <div class="stat-pill">
811
+ <span class="label">VERSION</span>
812
+ <span class="value">${profile.version}</span>
813
+ </div>
814
+ <div class="stat-pill">
815
+ <span class="label">PROMPTS</span>
816
+ <span class="value">${profile.totalPromptsAnalyzed}</span>
817
+ </div>
818
+ <div class="stat-pill">
819
+ <span class="label">LAST UPDATED</span>
820
+ <span class="value">${formatDate(profile.lastAnalyzedAt)}</span>
821
+ </div>
822
+ </div>
739
823
  </div>
740
- <button id="view-changelog-btn" class="btn-secondary">
741
- <i data-lucide="history" class="icon"></i> Version History
824
+ <button id="view-changelog-btn" class="btn-secondary compact">
825
+ <i data-lucide="history" class="icon"></i> History
742
826
  </button>
743
827
  </div>
744
828
 
745
- <div class="profile-section">
746
- <h4><i data-lucide="heart" class="icon"></i> Preferences (${preferences.length})</h4>
747
- ${
748
- preferences.length === 0
749
- ? '<p class="empty-text">No preferences learned yet</p>'
750
- : `
751
- <div class="preferences-list">
752
- ${preferences
753
- .map(
754
- (p) => `
755
- <div class="preference-item">
756
- <div class="preference-header">
757
- <span class="preference-name">${escapeHtml(p.description)}</span>
758
- <span class="confidence-badge">${Math.round(p.confidence * 100)}%</span>
759
- </div>
760
- <div class="confidence-bar">
761
- <div class="confidence-fill" style="width: ${p.confidence * 100}%"></div>
829
+ <div class="dashboard-grid">
830
+ <div class="dashboard-section preferences-section">
831
+ <h4><i data-lucide="heart" class="icon"></i> PREFERENCES <span class="count">${preferences.length}</span></h4>
832
+ ${
833
+ preferences.length === 0
834
+ ? '<p class="empty-text">No preferences learned yet</p>'
835
+ : `
836
+ <div class="cards-grid">
837
+ ${preferences
838
+ .sort((a, b) => b.confidence - a.confidence)
839
+ .map(
840
+ (p) => `
841
+ <div class="compact-card preference-card">
842
+ <div class="card-top">
843
+ <span class="category-tag">${escapeHtml(p.category)}</span>
844
+ <div class="confidence-ring" style="--p:${Math.round(p.confidence * 100)}">
845
+ <span>${Math.round(p.confidence * 100)}%</span>
846
+ </div>
847
+ </div>
848
+ <div class="card-body">
849
+ <p class="card-text">${escapeHtml(p.description)}</p>
850
+ </div>
851
+ ${
852
+ p.evidence && p.evidence.length > 0
853
+ ? `
854
+ <div class="card-footer">
855
+ <span class="evidence-toggle" title="${escapeHtml(Array.isArray(p.evidence) ? p.evidence.join("\n") : p.evidence)}">
856
+ <i data-lucide="info" class="icon-xs"></i> ${Array.isArray(p.evidence) ? p.evidence.length : 1} evidence
857
+ </span>
858
+ </div>`
859
+ : ""
860
+ }
762
861
  </div>
763
- <p class="preference-evidence">${escapeHtml(Array.isArray(p.evidence) ? p.evidence.join(", ") : p.evidence)}</p>
764
- <p class="preference-meta">Category: ${escapeHtml(p.category)}</p>
765
- </div>
766
- `
767
- )
768
- .join("")}
769
- </div>
770
- `
771
- }
772
- </div>
862
+ `
863
+ )
864
+ .join("")}
865
+ </div>
866
+ `
867
+ }
868
+ </div>
773
869
 
774
- <div class="profile-section">
775
- <h4><i data-lucide="activity" class="icon"></i> Patterns (${patterns.length})</h4>
776
- ${
777
- patterns.length === 0
778
- ? '<p class="empty-text">No patterns detected yet</p>'
779
- : `
780
- <div class="patterns-list">
781
- ${patterns
782
- .map(
783
- (p) => `
784
- <div class="pattern-item">
785
- <div class="pattern-header">
786
- <span class="pattern-name">${escapeHtml(p.description)}</span>
787
- <span class="category-badge">${escapeHtml(p.category)}</span>
870
+ <div class="dashboard-section patterns-section">
871
+ <h4><i data-lucide="activity" class="icon"></i> PATTERNS <span class="count">${patterns.length}</span></h4>
872
+ ${
873
+ patterns.length === 0
874
+ ? '<p class="empty-text">No patterns detected yet</p>'
875
+ : `
876
+ <div class="cards-grid">
877
+ ${patterns
878
+ .map(
879
+ (p) => `
880
+ <div class="compact-card pattern-card">
881
+ <div class="card-top">
882
+ <span class="category-tag">${escapeHtml(p.category)}</span>
883
+ </div>
884
+ <div class="card-body">
885
+ <p class="card-text">${escapeHtml(p.description)}</p>
886
+ </div>
788
887
  </div>
789
- </div>
790
- `
791
- )
792
- .join("")}
793
- </div>
794
- `
795
- }
796
- </div>
888
+ `
889
+ )
890
+ .join("")}
891
+ </div>
892
+ `
893
+ }
894
+ </div>
797
895
 
798
- <div class="profile-section">
799
- <h4><i data-lucide="workflow" class="icon"></i> Workflows (${workflows.length})</h4>
800
- ${
801
- workflows.length === 0
802
- ? '<p class="empty-text">No workflows identified yet</p>'
803
- : `
804
- <div class="workflows-list">
805
- ${workflows
806
- .map(
807
- (w) => `
808
- <div class="workflow-item">
809
- <div class="workflow-header">
810
- <span class="workflow-name">${escapeHtml(w.description)}</span>
811
- </div>
812
- <div class="workflow-steps">
813
- ${w.steps
814
- .map(
815
- (step) => `
816
- <div class="workflow-step">
817
- <span class="step-text">${escapeHtml(step)}</span>
818
- </div>
819
- `
820
- )
821
- .join("")}
896
+ <div class="dashboard-section workflows-section full-width">
897
+ <h4><i data-lucide="workflow" class="icon"></i> WORKFLOWS <span class="count">${workflows.length}</span></h4>
898
+ ${
899
+ workflows.length === 0
900
+ ? '<p class="empty-text">No workflows identified yet</p>'
901
+ : `
902
+ <div class="workflows-grid">
903
+ ${workflows
904
+ .map(
905
+ (w) => `
906
+ <div class="workflow-row">
907
+ <div class="workflow-title">${escapeHtml(w.description)}</div>
908
+ <div class="workflow-steps-horizontal">
909
+ ${w.steps
910
+ .map(
911
+ (step, i) => `
912
+ <div class="step-node">
913
+ <span class="step-idx">${i + 1}</span>
914
+ <span class="step-content">${escapeHtml(step)}</span>
915
+ </div>
916
+ ${i < w.steps.length - 1 ? '<i data-lucide="arrow-right" class="step-arrow"></i>' : ""}
917
+ `
918
+ )
919
+ .join("")}
920
+ </div>
822
921
  </div>
823
- </div>
824
- `
825
- )
826
- .join("")}
827
- </div>
828
- `
829
- }
830
- </div>
831
-
832
- <div class="profile-section">
833
- <h4><i data-lucide="award" class="icon"></i> Skill Level</h4>
834
- <div class="skill-level">
835
- <div class="skill-item">
836
- <span class="skill-label">Overall</span>
837
- <span class="skill-value">${escapeHtml(skillLevel.overall || "unknown")}</span>
838
- </div>
839
- ${Object.entries(skillLevel.domains || {})
840
- .map(
841
- ([domain, level]) => `
842
- <div class="skill-item">
843
- <span class="skill-label">${escapeHtml(domain)}</span>
844
- <span class="skill-value">${escapeHtml(level)}</span>
922
+ `
923
+ )
924
+ .join("")}
845
925
  </div>
846
926
  `
847
- )
848
- .join("")}
927
+ }
849
928
  </div>
850
929
  </div>
851
930
  `;