lore-memory 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,321 @@
1
+ :root {
2
+ --bg-color: #050505;
3
+ --panel-bg: #111111;
4
+ --text-main: #00FF41; /* CRT Green */
5
+ --text-muted: #008F11;
6
+ --accent-decision: #00FFFF; /* Cyan */
7
+ --accent-invariant: #FF003C; /* Red */
8
+ --accent-gotcha: #FFB000; /* Amber */
9
+ --accent-graveyard: #555555;
10
+ --border: 1px solid #008F11;
11
+ --font-pixel: 'Press Start 2P', cursive;
12
+ --font-code: 'Fira Code', 'Courier New', monospace;
13
+ --glitch-offset: 2px;
14
+ }
15
+
16
+ * {
17
+ box-sizing: border-box;
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+
22
+ body {
23
+ background-color: var(--bg-color);
24
+ color: var(--text-main);
25
+ font-family: var(--font-code);
26
+ font-size: 14px;
27
+ line-height: 1.6;
28
+ overflow: hidden; /* App feels like a terminal */
29
+ }
30
+
31
+ /* CRT Scanline Overlay Effect */
32
+ .crt-overlay {
33
+ position: fixed;
34
+ top: 0;
35
+ left: 0;
36
+ width: 100vw;
37
+ height: 100vh;
38
+ background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
39
+ background-size: 100% 4px, 3px 100%;
40
+ pointer-events: none;
41
+ z-index: 999;
42
+ }
43
+
44
+ /* Layout */
45
+ .layout {
46
+ display: flex;
47
+ height: 100vh;
48
+ }
49
+
50
+ /* Sidebar */
51
+ .sidebar {
52
+ width: 350px;
53
+ background-color: var(--panel-bg);
54
+ border-right: var(--border);
55
+ padding: 30px;
56
+ display: flex;
57
+ flex-direction: column;
58
+ }
59
+
60
+ .logo pre {
61
+ font-family: monospace;
62
+ color: var(--text-main);
63
+ font-size: 8px; /* Fit the ASCII art */
64
+ white-space: pre;
65
+ margin-bottom: 5px;
66
+ line-height: 1.2;
67
+ }
68
+
69
+ .subtitle {
70
+ font-family: var(--font-code);
71
+ color: var(--text-muted);
72
+ font-size: 12px;
73
+ letter-spacing: 2px;
74
+ margin-bottom: 40px;
75
+ text-align: center;
76
+ }
77
+
78
+ .nav-links {
79
+ list-style: none;
80
+ }
81
+
82
+ .nav-links li {
83
+ margin-bottom: 15px;
84
+ }
85
+
86
+ .nav-links a {
87
+ color: var(--text-muted);
88
+ text-decoration: none;
89
+ font-family: var(--font-pixel);
90
+ font-size: 10px;
91
+ display: block;
92
+ padding: 10px;
93
+ border: 1px solid transparent;
94
+ transition: all 0.2s ease;
95
+ }
96
+
97
+ .nav-links a:hover, .nav-links a.active {
98
+ color: var(--bg-color);
99
+ background-color: var(--text-main);
100
+ border: 1px solid var(--text-main);
101
+ box-shadow: 0 0 10px var(--text-main);
102
+ }
103
+
104
+ /* Main Content Area */
105
+ .content {
106
+ flex: 1;
107
+ padding: 40px;
108
+ overflow-y: auto;
109
+ position: relative;
110
+ }
111
+
112
+ .view {
113
+ display: none;
114
+ animation: flicker 0.15s ease-in 1;
115
+ }
116
+
117
+ .view.active {
118
+ display: block;
119
+ }
120
+
121
+ h1 {
122
+ font-family: var(--font-pixel);
123
+ font-size: 24px;
124
+ margin-bottom: 30px;
125
+ color: var(--text-main);
126
+ text-shadow: 0 0 5px var(--text-main);
127
+ }
128
+
129
+ /* Dashboard Score Components */
130
+ .score-card {
131
+ border: var(--border);
132
+ padding: 40px;
133
+ text-align: center;
134
+ margin-bottom: 30px;
135
+ background: repeating-linear-gradient(
136
+ 45deg,
137
+ transparent,
138
+ transparent 10px,
139
+ rgba(0, 143, 17, 0.05) 10px,
140
+ rgba(0, 143, 17, 0.05) 20px
141
+ );
142
+ }
143
+
144
+ .main-score {
145
+ font-family: var(--font-pixel);
146
+ font-size: 64px;
147
+ text-shadow: 0 0 15px var(--text-main);
148
+ }
149
+
150
+ .muted {
151
+ font-size: 24px;
152
+ color: var(--text-muted);
153
+ }
154
+
155
+ .metrics-grid {
156
+ display: grid;
157
+ grid-template-columns: 1fr 1fr 1fr;
158
+ gap: 20px;
159
+ margin-bottom: 40px;
160
+ }
161
+
162
+ .metric h3 {
163
+ font-family: var(--font-pixel);
164
+ font-size: 10px;
165
+ margin-bottom: 15px;
166
+ color: var(--text-muted);
167
+ }
168
+
169
+ .progress-bar {
170
+ height: 20px;
171
+ border: var(--border);
172
+ padding: 2px;
173
+ margin-bottom: 10px;
174
+ }
175
+
176
+ .progress-bar .fill {
177
+ height: 100%;
178
+ background-color: var(--text-main);
179
+ width: 0%;
180
+ transition: width 1s ease-out;
181
+ }
182
+
183
+ .tips-container {
184
+ border-top: var(--border);
185
+ padding-top: 20px;
186
+ }
187
+
188
+ .terminal-list {
189
+ list-style: none;
190
+ }
191
+ .terminal-list li::before {
192
+ content: "> ";
193
+ color: var(--accent-gotcha);
194
+ }
195
+
196
+ /* Knowledge Base Entries */
197
+ .controls {
198
+ display: flex;
199
+ gap: 15px;
200
+ margin-bottom: 20px;
201
+ }
202
+
203
+ input, select, button {
204
+ background: var(--bg-color);
205
+ color: var(--text-main);
206
+ border: var(--border);
207
+ padding: 10px;
208
+ font-family: var(--font-code);
209
+ font-size: 14px;
210
+ }
211
+
212
+ input:focus, select:focus {
213
+ outline: none;
214
+ box-shadow: 0 0 8px var(--text-muted);
215
+ }
216
+
217
+ .entries-grid, .drafts-container {
218
+ display: flex;
219
+ flex-direction: column;
220
+ gap: 20px;
221
+ }
222
+
223
+ .entry-card {
224
+ border: var(--border);
225
+ padding: 20px;
226
+ background-color: rgba(17, 17, 17, 0.8);
227
+ }
228
+
229
+ .entry-header {
230
+ display: flex;
231
+ justify-content: space-between;
232
+ border-bottom: 1px dashed var(--text-muted);
233
+ padding-bottom: 10px;
234
+ margin-bottom: 15px;
235
+ }
236
+
237
+ .type-badge {
238
+ font-family: var(--font-pixel);
239
+ font-size: 10px;
240
+ padding: 4px 8px;
241
+ }
242
+
243
+ .type-decision { color: var(--bg-color); background-color: var(--accent-decision); }
244
+ .type-invariant { color: var(--bg-color); background-color: var(--accent-invariant); }
245
+ .type-gotcha { color: var(--bg-color); background-color: var(--accent-gotcha); }
246
+ .type-graveyard { color: var(--text-main); background-color: var(--accent-graveyard); }
247
+
248
+ .entry-title {
249
+ font-weight: bold;
250
+ font-size: 16px;
251
+ margin-bottom: 10px;
252
+ }
253
+
254
+ .entry-meta {
255
+ color: var(--text-muted);
256
+ font-size: 12px;
257
+ margin-top: 15px;
258
+ }
259
+
260
+ /* Draft Specifics */
261
+ .draft-actions {
262
+ display: flex;
263
+ gap: 10px;
264
+ margin-top: 15px;
265
+ }
266
+
267
+ .btn {
268
+ cursor: pointer;
269
+ font-family: var(--font-pixel);
270
+ font-size: 8px;
271
+ text-transform: uppercase;
272
+ transition: all 0.2s;
273
+ }
274
+
275
+ .btn-accept {
276
+ border-color: var(--text-main);
277
+ color: var(--text-main);
278
+ }
279
+ .btn-accept:hover { background: var(--text-main); color: var(--bg-color); }
280
+
281
+ .btn-delete {
282
+ border-color: var(--accent-invariant);
283
+ color: var(--accent-invariant);
284
+ }
285
+ .btn-delete:hover { background: var(--accent-invariant); color: var(--bg-color); }
286
+
287
+ /* Graph Container */
288
+ #network-container {
289
+ width: 100%;
290
+ height: 600px;
291
+ border: var(--border);
292
+ background-color: var(--panel-bg);
293
+ }
294
+
295
+ /* Animations */
296
+ @keyframes flicker {
297
+ 0% { opacity: 0; }
298
+ 10% { opacity: 0.5; }
299
+ 20% { opacity: 0; }
300
+ 50% { opacity: 1; }
301
+ 60% { opacity: 0.8; }
302
+ 100% { opacity: 1; }
303
+ }
304
+
305
+ .toast {
306
+ position: fixed;
307
+ bottom: 20px;
308
+ right: 20px;
309
+ background-color: var(--text-main);
310
+ color: var(--bg-color);
311
+ padding: 15px 30px;
312
+ font-family: var(--font-code);
313
+ font-weight: bold;
314
+ border: 1px solid white;
315
+ z-index: 1000;
316
+ transition: opacity 0.3s;
317
+ }
318
+ .toast.hidden {
319
+ opacity: 0;
320
+ pointer-events: none;
321
+ }
@@ -3,7 +3,9 @@
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
5
  const { detectType, extractTitle, scoreComment } = require('../lib/nlp');
6
- const { saveDraft } = require('../lib/drafts');
6
+ const { saveDraft, listDrafts } = require('../lib/drafts');
7
+ const { readIndex } = require('../lib/index');
8
+ const { readEntry } = require('../lib/entries');
7
9
 
8
10
  /**
9
11
  * Extract raw comment strings from source code.
@@ -57,7 +59,11 @@ async function mineFile(absFilePath, projectRoot) {
57
59
  const comments = extractComments(code, absFilePath);
58
60
  const created = [];
59
61
 
60
- // Deduplicate: skip if we have a recent draft from same file with very similar title
62
+ const existingDrafts = listDrafts();
63
+ const index = readIndex();
64
+ const existingEntries = Object.values(index.entries).map(p => readEntry(p)).filter(Boolean);
65
+
66
+ // Deduplicate: skip if we have a recent draft or entry from same file with same title
61
67
  for (const comment of comments) {
62
68
  const score = scoreComment(comment);
63
69
  if (score < 0.5) continue;
@@ -66,6 +72,11 @@ async function mineFile(absFilePath, projectRoot) {
66
72
  const title = extractTitle(comment);
67
73
  if (!title || title.length < 3) continue;
68
74
 
75
+ const isDuplicateDraft = existingDrafts.some(d => d.suggestedTitle === title && (d.files || []).includes(relativePath));
76
+ const isDuplicateEntry = existingEntries.some(e => e.title === title && (e.files || []).includes(relativePath));
77
+
78
+ if (isDuplicateDraft || isDuplicateEntry) continue;
79
+
69
80
  const draft = {
70
81
  draftId: `draft-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
71
82
  suggestedType: type,
@@ -92,7 +103,7 @@ async function mineFile(absFilePath, projectRoot) {
92
103
  * @param {string[]} ignore
93
104
  * @returns {number} total drafts created
94
105
  */
95
- function mineDirectory(absDirPath, projectRoot, ignore) {
106
+ async function mineDirectory(absDirPath, projectRoot, ignore) {
96
107
  const { globSync } = require('glob');
97
108
  const ignoreList = ignore || ['node_modules', 'dist', '.git', '.lore', 'coverage'];
98
109
  const ignorePats = ignoreList.map(i => `${i}/**`);
@@ -105,7 +116,8 @@ function mineDirectory(absDirPath, projectRoot, ignore) {
105
116
 
106
117
  let total = 0;
107
118
  for (const file of files) {
108
- total += mineFile(file, projectRoot).length;
119
+ const drafts = await mineFile(file, projectRoot);
120
+ total += drafts.length;
109
121
  }
110
122
  return total;
111
123
  }