lore-memory 0.1.1 → 0.3.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.
- package/bin/lore.js +98 -1
- package/package.json +4 -2
- package/src/commands/drafts.js +61 -85
- package/src/commands/init.js +7 -4
- package/src/commands/log.js +1 -1
- package/src/commands/mine.js +4 -3
- package/src/commands/ui.js +171 -0
- package/src/lib/format.js +56 -5
- package/src/mcp/tools/overview.js +5 -0
- package/src/mcp/tools/search.js +11 -1
- package/src/ui/public/app.js +286 -0
- package/src/ui/public/index.html +118 -0
- package/src/ui/public/style.css +321 -0
- package/src/watcher/comments.js +6 -5
- package/src/watcher/index.js +18 -18
- package/src/watcher/signals.js +12 -12
|
@@ -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
|
+
}
|
package/src/watcher/comments.js
CHANGED
|
@@ -47,11 +47,11 @@ function extractComments(code, filePath) {
|
|
|
47
47
|
* Saves passing comments as drafts.
|
|
48
48
|
* @param {string} absFilePath
|
|
49
49
|
* @param {string} projectRoot
|
|
50
|
-
* @returns {object[]} created drafts
|
|
50
|
+
* @returns {Promise<object[]>} created drafts
|
|
51
51
|
*/
|
|
52
|
-
function mineFile(absFilePath, projectRoot) {
|
|
52
|
+
async function mineFile(absFilePath, projectRoot) {
|
|
53
53
|
let code = '';
|
|
54
|
-
try { code = fs.
|
|
54
|
+
try { code = await fs.readFile(absFilePath, 'utf8'); } catch (e) { return []; }
|
|
55
55
|
|
|
56
56
|
const relativePath = path.relative(projectRoot, absFilePath).replace(/\\/g, '/');
|
|
57
57
|
const comments = extractComments(code, absFilePath);
|
|
@@ -92,7 +92,7 @@ function mineFile(absFilePath, projectRoot) {
|
|
|
92
92
|
* @param {string[]} ignore
|
|
93
93
|
* @returns {number} total drafts created
|
|
94
94
|
*/
|
|
95
|
-
function mineDirectory(absDirPath, projectRoot, ignore) {
|
|
95
|
+
async function mineDirectory(absDirPath, projectRoot, ignore) {
|
|
96
96
|
const { globSync } = require('glob');
|
|
97
97
|
const ignoreList = ignore || ['node_modules', 'dist', '.git', '.lore', 'coverage'];
|
|
98
98
|
const ignorePats = ignoreList.map(i => `${i}/**`);
|
|
@@ -105,7 +105,8 @@ function mineDirectory(absDirPath, projectRoot, ignore) {
|
|
|
105
105
|
|
|
106
106
|
let total = 0;
|
|
107
107
|
for (const file of files) {
|
|
108
|
-
|
|
108
|
+
const drafts = await mineFile(file, projectRoot);
|
|
109
|
+
total += drafts.length;
|
|
109
110
|
}
|
|
110
111
|
return total;
|
|
111
112
|
}
|
package/src/watcher/index.js
CHANGED
|
@@ -29,9 +29,9 @@ function startWatcher(options = {}) {
|
|
|
29
29
|
|
|
30
30
|
const log = options.logFile
|
|
31
31
|
? (msg) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
const plain = msg.replace(/\x1B\[[0-9;]*m/g, '');
|
|
33
|
+
fs.appendFileSync(options.logFile, `${new Date().toISOString()} ${plain}\n`);
|
|
34
|
+
}
|
|
35
35
|
: (msg) => console.log(msg);
|
|
36
36
|
|
|
37
37
|
if (!options.quiet) {
|
|
@@ -56,46 +56,46 @@ function startWatcher(options = {}) {
|
|
|
56
56
|
awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
watcher.on('unlink', (relPath) => {
|
|
59
|
+
watcher.on('unlink', async (relPath) => {
|
|
60
60
|
const abs = path.join(projectRoot, relPath);
|
|
61
|
-
const draft = signals.onFileDeletion(abs, projectRoot);
|
|
61
|
+
const draft = await signals.onFileDeletion(abs, projectRoot);
|
|
62
62
|
if (draft) recordDraft(draft, abs);
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
watcher.on('unlinkDir', (relPath) => {
|
|
65
|
+
watcher.on('unlinkDir', async (relPath) => {
|
|
66
66
|
const abs = path.join(projectRoot, relPath);
|
|
67
|
-
const draft = signals.onDirectoryDeletion(abs, projectRoot);
|
|
67
|
+
const draft = await signals.onDirectoryDeletion(abs, projectRoot);
|
|
68
68
|
if (draft) recordDraft(draft, abs);
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
watcher.on('add', (relPath) => {
|
|
71
|
+
watcher.on('add', async (relPath) => {
|
|
72
72
|
const abs = path.join(projectRoot, relPath);
|
|
73
|
-
const draft = signals.onNewFile(abs, projectRoot);
|
|
73
|
+
const draft = await signals.onNewFile(abs, projectRoot);
|
|
74
74
|
if (draft) recordDraft(draft, abs);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
watcher.on('change', (relPath) => {
|
|
77
|
+
watcher.on('change', async (relPath) => {
|
|
78
78
|
const abs = path.join(projectRoot, relPath);
|
|
79
79
|
|
|
80
80
|
// Repeated edit tracking
|
|
81
|
-
const editDraft = signals.trackFileEdit(abs, projectRoot);
|
|
81
|
+
const editDraft = await signals.trackFileEdit(abs, projectRoot);
|
|
82
82
|
if (editDraft) recordDraft(editDraft, abs);
|
|
83
83
|
|
|
84
84
|
// package.json changes
|
|
85
85
|
if (relPath.endsWith('package.json')) {
|
|
86
|
-
const pkgDrafts = signals.onPackageJsonChange(abs, projectRoot);
|
|
86
|
+
const pkgDrafts = await signals.onPackageJsonChange(abs, projectRoot);
|
|
87
87
|
for (const d of pkgDrafts) recordDraft(d, abs);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// Comment mining + graph update for source files
|
|
91
91
|
if (/\.(js|ts|jsx|tsx|py|go|rs)$/.test(relPath)) {
|
|
92
|
-
const commentDrafts = mineFile(abs, projectRoot);
|
|
92
|
+
const commentDrafts = await mineFile(abs, projectRoot);
|
|
93
93
|
if (commentDrafts.length > 0) {
|
|
94
94
|
draftCount += commentDrafts.length;
|
|
95
95
|
log(`${chalk.dim(`[${timestamp()}]`)} Mined ${commentDrafts.length} comment${commentDrafts.length === 1 ? '' : 's'} from ${chalk.yellow(relPath)} — queued for review`);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
try { updateGraphForFile(abs, projectRoot); } catch (e) {}
|
|
98
|
+
try { updateGraphForFile(abs, projectRoot); } catch (e) { }
|
|
99
99
|
}
|
|
100
100
|
});
|
|
101
101
|
|
|
@@ -104,15 +104,15 @@ function startWatcher(options = {}) {
|
|
|
104
104
|
let gitWatcher = null;
|
|
105
105
|
if (fs.existsSync(path.join(projectRoot, '.git'))) {
|
|
106
106
|
gitWatcher = chokidar.watch(commitMsgPath, { persistent: true, ignoreInitial: true });
|
|
107
|
-
gitWatcher.on('change', () => {
|
|
107
|
+
gitWatcher.on('change', async () => {
|
|
108
108
|
try {
|
|
109
|
-
const message = fs.
|
|
110
|
-
const drafts = signals.onCommitMessage(message, projectRoot);
|
|
109
|
+
const message = await fs.readFile(commitMsgPath, 'utf8');
|
|
110
|
+
const drafts = await signals.onCommitMessage(message.trim(), projectRoot);
|
|
111
111
|
for (const d of drafts) {
|
|
112
112
|
draftCount++;
|
|
113
113
|
log(`${chalk.dim(`[${timestamp()}]`)} Commit signal: "${message.slice(0, 60)}" — queued for review`);
|
|
114
114
|
}
|
|
115
|
-
} catch (e) {}
|
|
115
|
+
} catch (e) { }
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
118
|
|
package/src/watcher/signals.js
CHANGED
|
@@ -22,7 +22,7 @@ function makeDraft(overrides) {
|
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function onFileDeletion(filepath, projectRoot) {
|
|
25
|
+
async function onFileDeletion(filepath, projectRoot) {
|
|
26
26
|
const relativePath = path.relative(projectRoot, filepath).replace(/\\/g, '/');
|
|
27
27
|
|
|
28
28
|
// Try to check line count via git
|
|
@@ -32,7 +32,7 @@ function onFileDeletion(filepath, projectRoot) {
|
|
|
32
32
|
encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
33
33
|
});
|
|
34
34
|
lines = parseInt(out.trim(), 10) || 0;
|
|
35
|
-
} catch (e) {}
|
|
35
|
+
} catch (e) { }
|
|
36
36
|
|
|
37
37
|
if (lines > 0 && lines < 100) return null;
|
|
38
38
|
|
|
@@ -48,7 +48,7 @@ function onFileDeletion(filepath, projectRoot) {
|
|
|
48
48
|
return draft;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function onDirectoryDeletion(dirpath, projectRoot) {
|
|
51
|
+
async function onDirectoryDeletion(dirpath, projectRoot) {
|
|
52
52
|
const relativePath = path.relative(projectRoot, dirpath).replace(/\\/g, '/');
|
|
53
53
|
const name = path.basename(relativePath);
|
|
54
54
|
const draft = makeDraft({
|
|
@@ -62,7 +62,7 @@ function onDirectoryDeletion(dirpath, projectRoot) {
|
|
|
62
62
|
return draft;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
function onNewFile(filepath, projectRoot) {
|
|
65
|
+
async function onNewFile(filepath, projectRoot) {
|
|
66
66
|
const relativePath = path.relative(projectRoot, filepath).replace(/\\/g, '/');
|
|
67
67
|
const name = path.basename(relativePath);
|
|
68
68
|
const lower = name.toLowerCase();
|
|
@@ -97,7 +97,7 @@ function onNewFile(filepath, projectRoot) {
|
|
|
97
97
|
return null;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function onPackageJsonChange(filepath, projectRoot) {
|
|
100
|
+
async function onPackageJsonChange(filepath, projectRoot) {
|
|
101
101
|
const relativePath = path.relative(projectRoot, filepath).replace(/\\/g, '/');
|
|
102
102
|
if (!relativePath.endsWith('package.json')) return [];
|
|
103
103
|
|
|
@@ -108,9 +108,9 @@ function onPackageJsonChange(filepath, projectRoot) {
|
|
|
108
108
|
encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
109
109
|
});
|
|
110
110
|
prev = JSON.parse(prevRaw);
|
|
111
|
-
} catch (e) {}
|
|
111
|
+
} catch (e) { }
|
|
112
112
|
|
|
113
|
-
try { curr = fs.
|
|
113
|
+
try { curr = await fs.readJson(filepath); } catch (e) { return []; }
|
|
114
114
|
|
|
115
115
|
const prevDeps = Object.assign({}, prev.dependencies || {}, prev.devDependencies || {});
|
|
116
116
|
const currDeps = Object.assign({}, curr.dependencies || {}, curr.devDependencies || {});
|
|
@@ -143,7 +143,7 @@ function onPackageJsonChange(filepath, projectRoot) {
|
|
|
143
143
|
return drafts;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
function onCommitMessage(message, projectRoot) {
|
|
146
|
+
async function onCommitMessage(message, projectRoot) {
|
|
147
147
|
const lower = message.toLowerCase();
|
|
148
148
|
const signals = [
|
|
149
149
|
{ re: /\b(replac|switch(ed|ing)|migrat)\b/, type: 'decision', confidence: 0.8 },
|
|
@@ -171,12 +171,12 @@ function onCommitMessage(message, projectRoot) {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
// Track repeated edits to detect gotcha-worthy files
|
|
174
|
-
function trackFileEdit(filepath, projectRoot) {
|
|
174
|
+
async function trackFileEdit(filepath, projectRoot) {
|
|
175
175
|
const relativePath = path.relative(projectRoot, filepath).replace(/\\/g, '/');
|
|
176
176
|
const statePath = path.join(LORE_DIR, 'watch-state.json');
|
|
177
177
|
|
|
178
178
|
let state = { edits: {} };
|
|
179
|
-
try { state = fs.
|
|
179
|
+
try { state = await fs.readJson(statePath); } catch (e) { }
|
|
180
180
|
if (!state.edits) state.edits = {};
|
|
181
181
|
|
|
182
182
|
const now = Date.now();
|
|
@@ -186,7 +186,7 @@ function trackFileEdit(filepath, projectRoot) {
|
|
|
186
186
|
state.edits[relativePath].push(now);
|
|
187
187
|
state.edits[relativePath] = state.edits[relativePath].filter(t => t > weekAgo);
|
|
188
188
|
|
|
189
|
-
try { fs.
|
|
189
|
+
try { await fs.writeJson(statePath, state, { spaces: 2 }); } catch (e) { }
|
|
190
190
|
|
|
191
191
|
if (state.edits[relativePath].length >= 5) {
|
|
192
192
|
const name = path.basename(relativePath);
|
|
@@ -201,7 +201,7 @@ function trackFileEdit(filepath, projectRoot) {
|
|
|
201
201
|
saveDraft(draft);
|
|
202
202
|
// Reset to avoid spam
|
|
203
203
|
state.edits[relativePath] = [];
|
|
204
|
-
try { fs.
|
|
204
|
+
try { await fs.writeJson(statePath, state, { spaces: 2 }); } catch (e) { }
|
|
205
205
|
return draft;
|
|
206
206
|
}
|
|
207
207
|
return null;
|