@phren/cli 0.0.10 → 0.0.11
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 +2 -8
- package/mcp/dist/cli-actions.js +5 -5
- package/mcp/dist/cli-config.js +334 -127
- package/mcp/dist/cli-govern.js +35 -63
- package/mcp/dist/cli-graph.js +3 -2
- package/mcp/dist/cli-hooks-globs.js +2 -1
- package/mcp/dist/cli-hooks-output.js +3 -3
- package/mcp/dist/cli-hooks.js +39 -32
- package/mcp/dist/cli-namespaces.js +15 -5
- package/mcp/dist/cli-search.js +2 -2
- package/mcp/dist/content-archive.js +2 -2
- package/mcp/dist/content-dedup.js +9 -9
- package/mcp/dist/embedding.js +7 -7
- package/mcp/dist/entrypoint.js +129 -102
- package/mcp/dist/governance-locks.js +6 -5
- package/mcp/dist/governance-policy.js +155 -2
- package/mcp/dist/governance-scores.js +3 -3
- package/mcp/dist/hooks.js +39 -18
- package/mcp/dist/index.js +4 -4
- package/mcp/dist/init-config.js +3 -24
- package/mcp/dist/init-setup.js +5 -5
- package/mcp/dist/init.js +170 -23
- package/mcp/dist/link-checksums.js +3 -2
- package/mcp/dist/link-context.js +1 -1
- package/mcp/dist/link-doctor.js +3 -3
- package/mcp/dist/link-skills.js +98 -12
- package/mcp/dist/link.js +17 -27
- package/mcp/dist/machine-identity.js +1 -9
- package/mcp/dist/mcp-config.js +247 -42
- package/mcp/dist/mcp-data.js +9 -9
- package/mcp/dist/mcp-extract-facts.js +1 -1
- package/mcp/dist/mcp-extract.js +2 -2
- package/mcp/dist/mcp-finding.js +6 -6
- package/mcp/dist/mcp-graph.js +11 -11
- package/mcp/dist/mcp-ops.js +18 -18
- package/mcp/dist/mcp-search.js +8 -8
- package/mcp/dist/memory-ui-page.js +23 -0
- package/mcp/dist/memory-ui-scripts.js +210 -27
- package/mcp/dist/memory-ui-server.js +115 -3
- package/mcp/dist/phren-paths.js +7 -7
- package/mcp/dist/profile-store.js +2 -2
- package/mcp/dist/project-config.js +63 -16
- package/mcp/dist/session-utils.js +3 -2
- package/mcp/dist/shared-fragment-graph.js +22 -21
- package/mcp/dist/shared-index.js +144 -105
- package/mcp/dist/shared-retrieval.js +19 -13
- package/mcp/dist/shared-search-fallback.js +13 -13
- package/mcp/dist/shared-sqljs.js +3 -2
- package/mcp/dist/shared.js +3 -3
- package/mcp/dist/shell-input.js +1 -1
- package/mcp/dist/shell-state-store.js +1 -1
- package/mcp/dist/shell-view.js +3 -2
- package/mcp/dist/shell.js +1 -1
- package/mcp/dist/skill-files.js +4 -10
- package/mcp/dist/skill-registry.js +3 -0
- package/mcp/dist/status.js +41 -13
- package/mcp/dist/task-hygiene.js +1 -1
- package/mcp/dist/telemetry.js +5 -4
- package/mcp/dist/update.js +1 -1
- package/mcp/dist/utils.js +3 -3
- package/package.json +2 -2
- package/starter/global/skills/audit.md +106 -0
- package/mcp/dist/shared-paths.js +0 -1
package/mcp/dist/shared-index.js
CHANGED
|
@@ -49,8 +49,8 @@ async function _drainEmbQueue() {
|
|
|
49
49
|
await cache.load();
|
|
50
50
|
}
|
|
51
51
|
catch (err) {
|
|
52
|
-
if ((process.env.PHREN_DEBUG
|
|
53
|
-
process.stderr.write(`[phren] embeddingQueue cacheLoad: ${
|
|
52
|
+
if ((process.env.PHREN_DEBUG))
|
|
53
|
+
process.stderr.write(`[phren] embeddingQueue cacheLoad: ${errorMessage(err)}\n`);
|
|
54
54
|
}
|
|
55
55
|
const model = getEmbeddingModel();
|
|
56
56
|
for (const { docPath, content } of docs) {
|
|
@@ -62,16 +62,16 @@ async function _drainEmbQueue() {
|
|
|
62
62
|
cache.set(docPath, getEmbeddingModel(), vec);
|
|
63
63
|
}
|
|
64
64
|
catch (err) {
|
|
65
|
-
if ((process.env.PHREN_DEBUG
|
|
66
|
-
process.stderr.write(`[phren] embeddingQueue embedText: ${
|
|
65
|
+
if ((process.env.PHREN_DEBUG))
|
|
66
|
+
process.stderr.write(`[phren] embeddingQueue embedText: ${errorMessage(err)}\n`);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
try {
|
|
70
70
|
await cache.flush();
|
|
71
71
|
}
|
|
72
72
|
catch (err) {
|
|
73
|
-
if ((process.env.PHREN_DEBUG
|
|
74
|
-
process.stderr.write(`[phren] embeddingQueue cacheFlush: ${
|
|
73
|
+
if ((process.env.PHREN_DEBUG))
|
|
74
|
+
process.stderr.write(`[phren] embeddingQueue cacheFlush: ${errorMessage(err)}\n`);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -133,8 +133,8 @@ function _resolveImportsRecursive(content, phrenPath, seen, depth) {
|
|
|
133
133
|
normalized = fs.realpathSync.native(resolved);
|
|
134
134
|
}
|
|
135
135
|
catch (err) {
|
|
136
|
-
if ((process.env.PHREN_DEBUG
|
|
137
|
-
process.stderr.write(`[phren] resolveImports realpath: ${
|
|
136
|
+
if ((process.env.PHREN_DEBUG))
|
|
137
|
+
process.stderr.write(`[phren] resolveImports realpath: ${errorMessage(err)}\n`);
|
|
138
138
|
return `<!-- @import not found: ${trimmed} -->`;
|
|
139
139
|
}
|
|
140
140
|
let normalizedGlobalRoot = globalRoot;
|
|
@@ -158,8 +158,8 @@ function _resolveImportsRecursive(content, phrenPath, seen, depth) {
|
|
|
158
158
|
return _resolveImportsRecursive(imported, phrenPath, childSeen, depth + 1);
|
|
159
159
|
}
|
|
160
160
|
catch (err) {
|
|
161
|
-
if ((process.env.PHREN_DEBUG
|
|
162
|
-
process.stderr.write(`[phren] resolveImports fileRead: ${
|
|
161
|
+
if ((process.env.PHREN_DEBUG))
|
|
162
|
+
process.stderr.write(`[phren] resolveImports fileRead: ${errorMessage(err)}\n`);
|
|
163
163
|
return `<!-- @import error: ${trimmed} -->`;
|
|
164
164
|
}
|
|
165
165
|
});
|
|
@@ -180,8 +180,8 @@ function touchSentinel(phrenPath) {
|
|
|
180
180
|
fs.writeFileSync(sentinelPath, Date.now().toString());
|
|
181
181
|
}
|
|
182
182
|
catch (err) {
|
|
183
|
-
if ((process.env.PHREN_DEBUG
|
|
184
|
-
process.stderr.write(`[phren] touchSentinel: ${
|
|
183
|
+
if ((process.env.PHREN_DEBUG))
|
|
184
|
+
process.stderr.write(`[phren] touchSentinel: ${errorMessage(err)}\n`);
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
@@ -197,8 +197,8 @@ function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
|
197
197
|
hash.update(`${f}:${stat.mtimeMs}:${stat.size}`);
|
|
198
198
|
}
|
|
199
199
|
catch (err) {
|
|
200
|
-
if ((process.env.PHREN_DEBUG
|
|
201
|
-
process.stderr.write(`[phren] computePhrenHash skip: ${
|
|
200
|
+
if ((process.env.PHREN_DEBUG))
|
|
201
|
+
process.stderr.write(`[phren] computePhrenHash skip: ${errorMessage(err)}\n`);
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
for (const configPath of topicConfigEntries) {
|
|
@@ -207,8 +207,8 @@ function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
|
207
207
|
hash.update(`topic-config:${configPath}:${stat.mtimeMs}:${stat.size}`);
|
|
208
208
|
}
|
|
209
209
|
catch (err) {
|
|
210
|
-
if ((process.env.PHREN_DEBUG
|
|
211
|
-
process.stderr.write(`[phren] computePhrenHash topicConfig: ${
|
|
210
|
+
if ((process.env.PHREN_DEBUG))
|
|
211
|
+
process.stderr.write(`[phren] computePhrenHash topicConfig: ${errorMessage(err)}\n`);
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
}
|
|
@@ -239,8 +239,8 @@ function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
catch (err) {
|
|
242
|
-
if ((process.env.PHREN_DEBUG
|
|
243
|
-
process.stderr.write(`[phren] computePhrenHash globDir: ${
|
|
242
|
+
if ((process.env.PHREN_DEBUG))
|
|
243
|
+
process.stderr.write(`[phren] computePhrenHash globDir: ${errorMessage(err)}\n`);
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
files.sort();
|
|
@@ -250,8 +250,8 @@ function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
|
250
250
|
hash.update(`${f}:${stat.mtimeMs}:${stat.size}`);
|
|
251
251
|
}
|
|
252
252
|
catch (err) {
|
|
253
|
-
if ((process.env.PHREN_DEBUG
|
|
254
|
-
process.stderr.write(`[phren] computePhrenHash skip: ${
|
|
253
|
+
if ((process.env.PHREN_DEBUG))
|
|
254
|
+
process.stderr.write(`[phren] computePhrenHash skip: ${errorMessage(err)}\n`);
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
for (const configPath of topicConfigEntries) {
|
|
@@ -260,8 +260,8 @@ function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
|
260
260
|
hash.update(`topic-config:${configPath}:${stat.mtimeMs}:${stat.size}`);
|
|
261
261
|
}
|
|
262
262
|
catch (err) {
|
|
263
|
-
if ((process.env.PHREN_DEBUG
|
|
264
|
-
process.stderr.write(`[phren] computePhrenHash topicConfig: ${
|
|
263
|
+
if ((process.env.PHREN_DEBUG))
|
|
264
|
+
process.stderr.write(`[phren] computePhrenHash topicConfig: ${errorMessage(err)}\n`);
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
267
|
}
|
|
@@ -271,8 +271,8 @@ function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
|
271
271
|
hash.update(`native:${mem.fullPath}:${stat.mtimeMs}:${stat.size}`);
|
|
272
272
|
}
|
|
273
273
|
catch (err) {
|
|
274
|
-
if ((process.env.PHREN_DEBUG
|
|
275
|
-
process.stderr.write(`[phren] computePhrenHash skip: ${
|
|
274
|
+
if ((process.env.PHREN_DEBUG))
|
|
275
|
+
process.stderr.write(`[phren] computePhrenHash skip: ${errorMessage(err)}\n`);
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
// Include global/ files (pulled via @import) so changes invalidate the cache
|
|
@@ -286,8 +286,8 @@ function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
|
286
286
|
hash.update(`global:${f}:${stat.mtimeMs}:${stat.size}`);
|
|
287
287
|
}
|
|
288
288
|
catch (err) {
|
|
289
|
-
if ((process.env.PHREN_DEBUG
|
|
290
|
-
process.stderr.write(`[phren] computePhrenHash skip: ${
|
|
289
|
+
if ((process.env.PHREN_DEBUG))
|
|
290
|
+
process.stderr.write(`[phren] computePhrenHash skip: ${errorMessage(err)}\n`);
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
}
|
|
@@ -299,8 +299,8 @@ function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
|
299
299
|
hash.update(`manual-links:${stat.mtimeMs}:${stat.size}`);
|
|
300
300
|
}
|
|
301
301
|
catch (err) {
|
|
302
|
-
if ((process.env.PHREN_DEBUG
|
|
303
|
-
process.stderr.write(`[phren] computePhrenHash skip: ${
|
|
302
|
+
if ((process.env.PHREN_DEBUG))
|
|
303
|
+
process.stderr.write(`[phren] computePhrenHash skip: ${errorMessage(err)}\n`);
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
const indexPolicyPath = path.join(phrenPath, ".governance", "index-policy.json");
|
|
@@ -310,8 +310,8 @@ function computePhrenHash(phrenPath, profile, preGlobbed) {
|
|
|
310
310
|
hash.update(`index-policy-file:${stat.mtimeMs}:${stat.size}`);
|
|
311
311
|
}
|
|
312
312
|
catch (err) {
|
|
313
|
-
if ((process.env.PHREN_DEBUG
|
|
314
|
-
process.stderr.write(`[phren] computePhrenHash skip: ${
|
|
313
|
+
if ((process.env.PHREN_DEBUG))
|
|
314
|
+
process.stderr.write(`[phren] computePhrenHash skip: ${errorMessage(err)}\n`);
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
317
|
if (profile)
|
|
@@ -334,8 +334,8 @@ function loadHashMap(phrenPath) {
|
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
336
|
catch (err) {
|
|
337
|
-
if ((process.env.PHREN_DEBUG
|
|
338
|
-
process.stderr.write(`[phren] loadHashMap: ${
|
|
337
|
+
if ((process.env.PHREN_DEBUG))
|
|
338
|
+
process.stderr.write(`[phren] loadHashMap: ${errorMessage(err)}\n`);
|
|
339
339
|
}
|
|
340
340
|
return { hashes: {} };
|
|
341
341
|
}
|
|
@@ -355,8 +355,8 @@ function saveHashMap(phrenPath, hashes) {
|
|
|
355
355
|
existing = data.hashes;
|
|
356
356
|
}
|
|
357
357
|
catch (err) {
|
|
358
|
-
if ((process.env.PHREN_DEBUG
|
|
359
|
-
process.stderr.write(`[phren] saveHashMap readExisting: ${
|
|
358
|
+
if ((process.env.PHREN_DEBUG))
|
|
359
|
+
process.stderr.write(`[phren] saveHashMap readExisting: ${errorMessage(err)}\n`);
|
|
360
360
|
}
|
|
361
361
|
const merged = { ...existing, ...hashes };
|
|
362
362
|
// Remove entries for paths that no longer exist on disk
|
|
@@ -494,8 +494,8 @@ function insertFileIntoIndex(db, entry, phrenPath, opts) {
|
|
|
494
494
|
return true;
|
|
495
495
|
}
|
|
496
496
|
catch (err) {
|
|
497
|
-
if ((process.env.PHREN_DEBUG
|
|
498
|
-
process.stderr.write(`[phren] insertFileIntoIndex: ${
|
|
497
|
+
if ((process.env.PHREN_DEBUG))
|
|
498
|
+
process.stderr.write(`[phren] insertFileIntoIndex: ${errorMessage(err)}\n`);
|
|
499
499
|
return false;
|
|
500
500
|
}
|
|
501
501
|
}
|
|
@@ -575,8 +575,8 @@ function deleteEntityLinksForDocPath(db, phrenPath, docPath, fallbackProject, fa
|
|
|
575
575
|
db.run("DELETE FROM global_entities WHERE doc_key = ?", [sourceDoc]);
|
|
576
576
|
}
|
|
577
577
|
catch (err) {
|
|
578
|
-
if ((process.env.PHREN_DEBUG
|
|
579
|
-
process.stderr.write(`[phren] deleteEntityLinksForDocPath globalEntities: ${
|
|
578
|
+
if ((process.env.PHREN_DEBUG))
|
|
579
|
+
process.stderr.write(`[phren] deleteEntityLinksForDocPath globalEntities: ${errorMessage(err)}\n`);
|
|
580
580
|
}
|
|
581
581
|
}
|
|
582
582
|
/**
|
|
@@ -591,15 +591,15 @@ export function updateFileInIndex(db, filePath, phrenPath) {
|
|
|
591
591
|
deleteEntityLinksForDocPath(db, phrenPath, resolvedPath);
|
|
592
592
|
}
|
|
593
593
|
catch (err) {
|
|
594
|
-
if ((process.env.PHREN_DEBUG
|
|
595
|
-
process.stderr.write(`[phren] updateFileInIndex deleteEntityLinks: ${
|
|
594
|
+
if ((process.env.PHREN_DEBUG))
|
|
595
|
+
process.stderr.write(`[phren] updateFileInIndex deleteEntityLinks: ${errorMessage(err)}\n`);
|
|
596
596
|
}
|
|
597
597
|
try {
|
|
598
598
|
db.run("DELETE FROM docs WHERE path = ?", [resolvedPath]);
|
|
599
599
|
}
|
|
600
600
|
catch (err) {
|
|
601
|
-
if ((process.env.PHREN_DEBUG
|
|
602
|
-
process.stderr.write(`[phren] updateFileInIndex deleteDocs: ${
|
|
601
|
+
if ((process.env.PHREN_DEBUG))
|
|
602
|
+
process.stderr.write(`[phren] updateFileInIndex deleteDocs: ${errorMessage(err)}\n`);
|
|
603
603
|
}
|
|
604
604
|
// Re-insert if file still exists
|
|
605
605
|
if (fs.existsSync(resolvedPath)) {
|
|
@@ -618,8 +618,8 @@ export function updateFileInIndex(db, filePath, phrenPath) {
|
|
|
618
618
|
extractAndLinkFragments(db, content, getEntrySourceDocKey(entry, phrenPath), phrenPath);
|
|
619
619
|
}
|
|
620
620
|
catch (err) {
|
|
621
|
-
if ((process.env.PHREN_DEBUG
|
|
622
|
-
process.stderr.write(`[phren] updateFileInIndex entityExtraction: ${
|
|
621
|
+
if ((process.env.PHREN_DEBUG))
|
|
622
|
+
process.stderr.write(`[phren] updateFileInIndex entityExtraction: ${errorMessage(err)}\n`);
|
|
623
623
|
}
|
|
624
624
|
}
|
|
625
625
|
}
|
|
@@ -630,8 +630,8 @@ export function updateFileInIndex(db, filePath, phrenPath) {
|
|
|
630
630
|
saveHashMap(phrenPath, hashData.hashes);
|
|
631
631
|
}
|
|
632
632
|
catch (err) {
|
|
633
|
-
if ((process.env.PHREN_DEBUG
|
|
634
|
-
process.stderr.write(`[phren] updateFileInIndex hashMap: ${
|
|
633
|
+
if ((process.env.PHREN_DEBUG))
|
|
634
|
+
process.stderr.write(`[phren] updateFileInIndex hashMap: ${errorMessage(err)}\n`);
|
|
635
635
|
}
|
|
636
636
|
}
|
|
637
637
|
else {
|
|
@@ -644,8 +644,8 @@ export function updateFileInIndex(db, filePath, phrenPath) {
|
|
|
644
644
|
await c.flush();
|
|
645
645
|
}
|
|
646
646
|
catch (err) {
|
|
647
|
-
if ((process.env.PHREN_DEBUG
|
|
648
|
-
process.stderr.write(`[phren] updateFileInIndex embeddingDelete: ${
|
|
647
|
+
if ((process.env.PHREN_DEBUG))
|
|
648
|
+
process.stderr.write(`[phren] updateFileInIndex embeddingDelete: ${errorMessage(err)}\n`);
|
|
649
649
|
}
|
|
650
650
|
})();
|
|
651
651
|
}
|
|
@@ -664,8 +664,8 @@ function readHashSentinel(phrenPath) {
|
|
|
664
664
|
}
|
|
665
665
|
}
|
|
666
666
|
catch (err) {
|
|
667
|
-
if ((process.env.PHREN_DEBUG
|
|
668
|
-
process.stderr.write(`[phren] readHashSentinel: ${
|
|
667
|
+
if ((process.env.PHREN_DEBUG))
|
|
668
|
+
process.stderr.write(`[phren] readHashSentinel: ${errorMessage(err)}\n`);
|
|
669
669
|
}
|
|
670
670
|
return null;
|
|
671
671
|
}
|
|
@@ -675,8 +675,8 @@ function writeHashSentinel(phrenPath, hash) {
|
|
|
675
675
|
fs.writeFileSync(sentinelPath, JSON.stringify({ hash, computedAt: Date.now() }));
|
|
676
676
|
}
|
|
677
677
|
catch (err) {
|
|
678
|
-
if ((process.env.PHREN_DEBUG
|
|
679
|
-
process.stderr.write(`[phren] writeHashSentinel: ${
|
|
678
|
+
if ((process.env.PHREN_DEBUG))
|
|
679
|
+
process.stderr.write(`[phren] writeHashSentinel: ${errorMessage(err)}\n`);
|
|
680
680
|
}
|
|
681
681
|
}
|
|
682
682
|
function isSentinelFresh(phrenPath, sentinel) {
|
|
@@ -693,8 +693,8 @@ function isSentinelFresh(phrenPath, sentinel) {
|
|
|
693
693
|
return false;
|
|
694
694
|
}
|
|
695
695
|
catch (err) {
|
|
696
|
-
if ((process.env.PHREN_DEBUG
|
|
697
|
-
process.stderr.write(`[phren] isSentinelFresh statDir: ${
|
|
696
|
+
if ((process.env.PHREN_DEBUG))
|
|
697
|
+
process.stderr.write(`[phren] isSentinelFresh statDir: ${errorMessage(err)}\n`);
|
|
698
698
|
}
|
|
699
699
|
}
|
|
700
700
|
return true;
|
|
@@ -715,8 +715,8 @@ function loadCachedEntityGraph(db, graphPath, allFiles, phrenPath) {
|
|
|
715
715
|
return fs.statSync(f.fullPath).mtimeMs > graphMtime;
|
|
716
716
|
}
|
|
717
717
|
catch (err) {
|
|
718
|
-
if ((process.env.PHREN_DEBUG
|
|
719
|
-
process.stderr.write(`[phren] loadCachedEntityGraph statFile: ${
|
|
718
|
+
if ((process.env.PHREN_DEBUG))
|
|
719
|
+
process.stderr.write(`[phren] loadCachedEntityGraph statFile: ${errorMessage(err)}\n`);
|
|
720
720
|
return true;
|
|
721
721
|
}
|
|
722
722
|
});
|
|
@@ -743,8 +743,8 @@ function loadCachedEntityGraph(db, graphPath, allFiles, phrenPath) {
|
|
|
743
743
|
db.run("INSERT OR IGNORE INTO global_entities (entity, project, doc_key) VALUES (?, ?, ?)", [entity, project, docKey]);
|
|
744
744
|
}
|
|
745
745
|
catch (err) {
|
|
746
|
-
if ((process.env.PHREN_DEBUG
|
|
747
|
-
process.stderr.write(`[phren] loadCachedEntityGraph globalEntitiesInsert2: ${
|
|
746
|
+
if ((process.env.PHREN_DEBUG))
|
|
747
|
+
process.stderr.write(`[phren] loadCachedEntityGraph globalEntitiesInsert2: ${errorMessage(err)}\n`);
|
|
748
748
|
}
|
|
749
749
|
}
|
|
750
750
|
}
|
|
@@ -762,23 +762,23 @@ function loadCachedEntityGraph(db, graphPath, allFiles, phrenPath) {
|
|
|
762
762
|
db.run("INSERT OR IGNORE INTO global_entities (entity, project, doc_key) VALUES (?, ?, ?)", [name, proj, sourceDoc]);
|
|
763
763
|
}
|
|
764
764
|
catch (err) {
|
|
765
|
-
if ((process.env.PHREN_DEBUG
|
|
766
|
-
process.stderr.write(`[phren] loadCachedEntityGraph globalEntitiesInsert: ${
|
|
765
|
+
if ((process.env.PHREN_DEBUG))
|
|
766
|
+
process.stderr.write(`[phren] loadCachedEntityGraph globalEntitiesInsert: ${errorMessage(err)}\n`);
|
|
767
767
|
}
|
|
768
768
|
}
|
|
769
769
|
}
|
|
770
770
|
}
|
|
771
771
|
catch (err) {
|
|
772
|
-
if ((process.env.PHREN_DEBUG
|
|
773
|
-
process.stderr.write(`[phren] entityGraph globalEntitiesRestore: ${
|
|
772
|
+
if ((process.env.PHREN_DEBUG))
|
|
773
|
+
process.stderr.write(`[phren] entityGraph globalEntitiesRestore: ${errorMessage(err)}\n`);
|
|
774
774
|
}
|
|
775
775
|
}
|
|
776
776
|
return true;
|
|
777
777
|
}
|
|
778
778
|
}
|
|
779
779
|
catch (err) {
|
|
780
|
-
if ((process.env.PHREN_DEBUG
|
|
781
|
-
process.stderr.write(`[phren] entityGraph cacheLoad: ${
|
|
780
|
+
if ((process.env.PHREN_DEBUG))
|
|
781
|
+
process.stderr.write(`[phren] entityGraph cacheLoad: ${errorMessage(err)}\n`);
|
|
782
782
|
}
|
|
783
783
|
return false;
|
|
784
784
|
}
|
|
@@ -797,7 +797,7 @@ function mergeManualLinks(db, phrenPath) {
|
|
|
797
797
|
// Validate: skip manual links whose sourceDoc no longer exists in the index
|
|
798
798
|
const docCheck = queryDocBySourceKey(db, phrenPath, link.sourceDoc);
|
|
799
799
|
if (!docCheck) {
|
|
800
|
-
if ((process.env.PHREN_DEBUG
|
|
800
|
+
if ((process.env.PHREN_DEBUG))
|
|
801
801
|
process.stderr.write(`[phren] manualLinks: pruning stale link to "${link.sourceDoc}"\n`);
|
|
802
802
|
pruned = true;
|
|
803
803
|
continue;
|
|
@@ -819,14 +819,14 @@ function mergeManualLinks(db, phrenPath) {
|
|
|
819
819
|
db.run("INSERT OR IGNORE INTO global_entities (entity, project, doc_key) VALUES (?, ?, ?)", [link.entity, projectMatch[1], link.sourceDoc]);
|
|
820
820
|
}
|
|
821
821
|
catch (err) {
|
|
822
|
-
if ((process.env.PHREN_DEBUG
|
|
823
|
-
process.stderr.write(`[phren] manualLinks globalEntities: ${
|
|
822
|
+
if ((process.env.PHREN_DEBUG))
|
|
823
|
+
process.stderr.write(`[phren] manualLinks globalEntities: ${errorMessage(err)}\n`);
|
|
824
824
|
}
|
|
825
825
|
}
|
|
826
826
|
}
|
|
827
827
|
catch (err) {
|
|
828
|
-
if ((process.env.PHREN_DEBUG
|
|
829
|
-
process.stderr.write(`[phren] manualLinks entry: ${
|
|
828
|
+
if ((process.env.PHREN_DEBUG))
|
|
829
|
+
process.stderr.write(`[phren] manualLinks entry: ${errorMessage(err)}\n`);
|
|
830
830
|
}
|
|
831
831
|
}
|
|
832
832
|
// Rewrite manual-links.json if stale entries were pruned
|
|
@@ -839,14 +839,14 @@ function mergeManualLinks(db, phrenPath) {
|
|
|
839
839
|
});
|
|
840
840
|
}
|
|
841
841
|
catch (err) {
|
|
842
|
-
if ((process.env.PHREN_DEBUG
|
|
843
|
-
process.stderr.write(`[phren] manualLinks prune write: ${
|
|
842
|
+
if ((process.env.PHREN_DEBUG))
|
|
843
|
+
process.stderr.write(`[phren] manualLinks prune write: ${errorMessage(err)}\n`);
|
|
844
844
|
}
|
|
845
845
|
}
|
|
846
846
|
}
|
|
847
847
|
catch (err) {
|
|
848
|
-
if ((process.env.PHREN_DEBUG
|
|
849
|
-
process.stderr.write(`[phren] mergeManualLinks: ${
|
|
848
|
+
if ((process.env.PHREN_DEBUG))
|
|
849
|
+
process.stderr.write(`[phren] mergeManualLinks: ${errorMessage(err)}\n`);
|
|
850
850
|
}
|
|
851
851
|
}
|
|
852
852
|
async function buildIndexImpl(phrenPath, profile) {
|
|
@@ -859,21 +859,21 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
859
859
|
userSuffix = String(os.userInfo().uid);
|
|
860
860
|
}
|
|
861
861
|
catch (err) {
|
|
862
|
-
if ((process.env.PHREN_DEBUG
|
|
863
|
-
process.stderr.write(`[phren] buildIndexImpl userInfo: ${
|
|
862
|
+
if ((process.env.PHREN_DEBUG))
|
|
863
|
+
process.stderr.write(`[phren] buildIndexImpl userInfo: ${errorMessage(err)}\n`);
|
|
864
864
|
userSuffix = crypto.createHash("sha1").update(homeDir()).digest("hex").slice(0, 12);
|
|
865
865
|
}
|
|
866
866
|
const cacheDir = path.join(os.tmpdir(), `phren-fts-${userSuffix}`);
|
|
867
867
|
// Fast path: if the sentinel is fresh, skip the expensive glob + hash computation
|
|
868
868
|
const sentinel = readHashSentinel(phrenPath);
|
|
869
869
|
let hash;
|
|
870
|
-
let globResult;
|
|
870
|
+
let globResult = null;
|
|
871
871
|
if (sentinel && isSentinelFresh(phrenPath, sentinel)) {
|
|
872
872
|
hash = sentinel.hash;
|
|
873
873
|
const cacheFile = path.join(cacheDir, `${hash}.db`);
|
|
874
874
|
if (fs.existsSync(cacheFile)) {
|
|
875
|
-
// Sentinel cache hit — defer
|
|
876
|
-
globResult =
|
|
875
|
+
// Sentinel cache hit — defer glob; only load it if incremental path needs it
|
|
876
|
+
globResult = null;
|
|
877
877
|
}
|
|
878
878
|
else {
|
|
879
879
|
// Cache file was cleaned up, fall through to full computation
|
|
@@ -896,6 +896,39 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
896
896
|
const schemaChanged = savedHashData.version !== INDEX_SCHEMA_VERSION;
|
|
897
897
|
// Try loading cached DB for incremental update
|
|
898
898
|
if (!schemaChanged && fs.existsSync(cacheFile)) {
|
|
899
|
+
// Pure sentinel hit: glob was skipped, return DB without computing file diffs
|
|
900
|
+
if (!globResult) {
|
|
901
|
+
try {
|
|
902
|
+
const cached = fs.readFileSync(cacheFile);
|
|
903
|
+
const db = new SQL.Database(cached);
|
|
904
|
+
const docCountResult = db.exec("SELECT COUNT(*) FROM docs");
|
|
905
|
+
const docCount = docCountResult?.[0]?.values?.[0]?.[0] ?? 0;
|
|
906
|
+
if (docCount > 0) {
|
|
907
|
+
try {
|
|
908
|
+
db.run("ALTER TABLE entities ADD COLUMN first_seen_at TEXT");
|
|
909
|
+
}
|
|
910
|
+
catch { /* column already exists */ }
|
|
911
|
+
debugLog(`Loaded FTS index from cache (${hash.slice(0, 8)}) in ${Date.now() - t0}ms [sentinel-hit]`);
|
|
912
|
+
appendIndexEvent(phrenPath, {
|
|
913
|
+
event: "build_index",
|
|
914
|
+
cache: "hit",
|
|
915
|
+
hash: hash.slice(0, 12),
|
|
916
|
+
elapsedMs: Date.now() - t0,
|
|
917
|
+
profile: profile || "",
|
|
918
|
+
});
|
|
919
|
+
return db;
|
|
920
|
+
}
|
|
921
|
+
// Empty DB — fall through to full rebuild with glob
|
|
922
|
+
db.close();
|
|
923
|
+
}
|
|
924
|
+
catch (err) {
|
|
925
|
+
debugLog(`sentinel-hit DB load failed, falling back to full rebuild: ${errorMessage(err)}`);
|
|
926
|
+
}
|
|
927
|
+
// Need glob for rebuild
|
|
928
|
+
globResult = globAllFiles(phrenPath, profile);
|
|
929
|
+
hash = computePhrenHash(phrenPath, profile, globResult.filePaths);
|
|
930
|
+
writeHashSentinel(phrenPath, hash);
|
|
931
|
+
}
|
|
899
932
|
try {
|
|
900
933
|
const cached = fs.readFileSync(cacheFile);
|
|
901
934
|
let db;
|
|
@@ -932,8 +965,8 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
932
965
|
}
|
|
933
966
|
}
|
|
934
967
|
catch (err) {
|
|
935
|
-
if ((process.env.PHREN_DEBUG
|
|
936
|
-
process.stderr.write(`[phren] buildIndex hashFile: ${
|
|
968
|
+
if ((process.env.PHREN_DEBUG))
|
|
969
|
+
process.stderr.write(`[phren] buildIndex hashFile: ${errorMessage(err)}\n`);
|
|
937
970
|
}
|
|
938
971
|
}
|
|
939
972
|
// Check for files missing from the index (deleted files)
|
|
@@ -968,27 +1001,27 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
968
1001
|
deleteEntityLinksForDocPath(db, phrenPath, missingPath);
|
|
969
1002
|
}
|
|
970
1003
|
catch (err) {
|
|
971
|
-
if ((process.env.PHREN_DEBUG
|
|
972
|
-
process.stderr.write(`[phren] buildIndex deleteEntityLinksForMissing: ${
|
|
1004
|
+
if ((process.env.PHREN_DEBUG))
|
|
1005
|
+
process.stderr.write(`[phren] buildIndex deleteEntityLinksForMissing: ${errorMessage(err)}\n`);
|
|
973
1006
|
}
|
|
974
1007
|
try {
|
|
975
1008
|
db.run("DELETE FROM docs WHERE path = ?", [missingPath]);
|
|
976
1009
|
}
|
|
977
1010
|
catch (err) {
|
|
978
|
-
if ((process.env.PHREN_DEBUG
|
|
979
|
-
process.stderr.write(`[phren] buildIndex deleteDocForMissing: ${
|
|
1011
|
+
if ((process.env.PHREN_DEBUG))
|
|
1012
|
+
process.stderr.write(`[phren] buildIndex deleteDocForMissing: ${errorMessage(err)}\n`);
|
|
980
1013
|
}
|
|
981
1014
|
}
|
|
982
1015
|
db.run("COMMIT");
|
|
983
1016
|
}
|
|
984
1017
|
catch (err) {
|
|
985
|
-
if ((process.env.PHREN_DEBUG
|
|
986
|
-
process.stderr.write(`[phren] buildIndex incrementalDeleteCommit: ${
|
|
1018
|
+
if ((process.env.PHREN_DEBUG))
|
|
1019
|
+
process.stderr.write(`[phren] buildIndex incrementalDeleteCommit: ${errorMessage(err)}\n`);
|
|
987
1020
|
try {
|
|
988
1021
|
db.run("ROLLBACK");
|
|
989
1022
|
}
|
|
990
1023
|
catch (e2) {
|
|
991
|
-
if ((process.env.PHREN_DEBUG
|
|
1024
|
+
if ((process.env.PHREN_DEBUG))
|
|
992
1025
|
process.stderr.write(`[phren] buildIndex incrementalDeleteRollback: ${e2 instanceof Error ? e2.message : String(e2)}\n`);
|
|
993
1026
|
}
|
|
994
1027
|
}
|
|
@@ -1025,7 +1058,7 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
1025
1058
|
db.run("ROLLBACK");
|
|
1026
1059
|
}
|
|
1027
1060
|
catch (e2) {
|
|
1028
|
-
if ((process.env.PHREN_DEBUG
|
|
1061
|
+
if ((process.env.PHREN_DEBUG))
|
|
1029
1062
|
process.stderr.write(`[phren] buildIndex perFileRollback: ${e2 instanceof Error ? e2.message : String(e2)}\n`);
|
|
1030
1063
|
}
|
|
1031
1064
|
throw err;
|
|
@@ -1040,8 +1073,8 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
1040
1073
|
fs.writeFileSync(cacheFile, db.export());
|
|
1041
1074
|
}
|
|
1042
1075
|
catch (err) {
|
|
1043
|
-
if ((process.env.PHREN_DEBUG
|
|
1044
|
-
process.stderr.write(`[phren] buildIndex incrementalCacheSave: ${
|
|
1076
|
+
if ((process.env.PHREN_DEBUG))
|
|
1077
|
+
process.stderr.write(`[phren] buildIndex incrementalCacheSave: ${errorMessage(err)}\n`);
|
|
1045
1078
|
}
|
|
1046
1079
|
const incMs = Date.now() - t0;
|
|
1047
1080
|
debugLog(`Incremental FTS update: ${updatedCount} changed, ${missingFromIndex.length} removed in ${incMs}ms`);
|
|
@@ -1069,6 +1102,12 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
1069
1102
|
}
|
|
1070
1103
|
}
|
|
1071
1104
|
// ── Full rebuild ──────────────────────────────────────────────────────────
|
|
1105
|
+
// Ensure glob data is available for full rebuild (may be null from sentinel fast-path fallback)
|
|
1106
|
+
if (!globResult) {
|
|
1107
|
+
globResult = globAllFiles(phrenPath, profile);
|
|
1108
|
+
hash = computePhrenHash(phrenPath, profile, globResult.filePaths);
|
|
1109
|
+
writeHashSentinel(phrenPath, hash);
|
|
1110
|
+
}
|
|
1072
1111
|
const db = new SQL.Database();
|
|
1073
1112
|
db.run(`
|
|
1074
1113
|
CREATE VIRTUAL TABLE docs USING fts5(
|
|
@@ -1092,8 +1131,8 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
1092
1131
|
newHashes[entry.fullPath] = hashFileContent(entry.fullPath);
|
|
1093
1132
|
}
|
|
1094
1133
|
catch (err) {
|
|
1095
|
-
if ((process.env.PHREN_DEBUG
|
|
1096
|
-
process.stderr.write(`[phren] computePhrenHash skip: ${
|
|
1134
|
+
if ((process.env.PHREN_DEBUG))
|
|
1135
|
+
process.stderr.write(`[phren] computePhrenHash skip: ${errorMessage(err)}\n`);
|
|
1097
1136
|
}
|
|
1098
1137
|
if (insertFileIntoIndex(db, entry, phrenPath, { scheduleEmbeddings: true })) {
|
|
1099
1138
|
fileCount++;
|
|
@@ -1120,8 +1159,8 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
1120
1159
|
fs.writeFileSync(graphPath, JSON.stringify({ entities: entityRows, links: linkRows, globalEntities: globalEntityRows, ts: Date.now() }));
|
|
1121
1160
|
}
|
|
1122
1161
|
catch (err) {
|
|
1123
|
-
if ((process.env.PHREN_DEBUG
|
|
1124
|
-
process.stderr.write(`[phren] buildIndex entityGraphPersist: ${
|
|
1162
|
+
if ((process.env.PHREN_DEBUG))
|
|
1163
|
+
process.stderr.write(`[phren] buildIndex entityGraphPersist: ${errorMessage(err)}\n`);
|
|
1125
1164
|
}
|
|
1126
1165
|
}
|
|
1127
1166
|
// Always merge manual links (survive rebuild)
|
|
@@ -1132,7 +1171,7 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
1132
1171
|
invalidateDfCache();
|
|
1133
1172
|
const buildMs = Date.now() - t0;
|
|
1134
1173
|
debugLog(`Built FTS index: ${fileCount} files from ${getProjectDirs(phrenPath, profile).length} projects in ${buildMs}ms`);
|
|
1135
|
-
if ((process.env.PHREN_DEBUG
|
|
1174
|
+
if ((process.env.PHREN_DEBUG))
|
|
1136
1175
|
console.error(`Indexed ${fileCount} files from ${getProjectDirs(phrenPath, profile).length} projects`);
|
|
1137
1176
|
appendIndexEvent(phrenPath, {
|
|
1138
1177
|
event: "build_index",
|
|
@@ -1153,8 +1192,8 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
1153
1192
|
fs.unlinkSync(path.join(cacheDir, f));
|
|
1154
1193
|
}
|
|
1155
1194
|
catch (err) {
|
|
1156
|
-
if ((process.env.PHREN_DEBUG
|
|
1157
|
-
process.stderr.write(`[phren] buildIndex staleCacheCleanup: ${
|
|
1195
|
+
if ((process.env.PHREN_DEBUG))
|
|
1196
|
+
process.stderr.write(`[phren] buildIndex staleCacheCleanup: ${errorMessage(err)}\n`);
|
|
1158
1197
|
}
|
|
1159
1198
|
}
|
|
1160
1199
|
debugLog(`Saved FTS index cache (${hash.slice(0, 8)}) — total ${Date.now() - t0}ms`);
|
|
@@ -1190,8 +1229,8 @@ function isRebuildLockHeld(phrenPath) {
|
|
|
1190
1229
|
return Date.now() - stat.mtimeMs <= staleThreshold;
|
|
1191
1230
|
}
|
|
1192
1231
|
catch (err) {
|
|
1193
|
-
if ((process.env.PHREN_DEBUG
|
|
1194
|
-
process.stderr.write(`[phren] isRebuildLockHeld stat: ${
|
|
1232
|
+
if ((process.env.PHREN_DEBUG))
|
|
1233
|
+
process.stderr.write(`[phren] isRebuildLockHeld stat: ${errorMessage(err)}\n`);
|
|
1195
1234
|
return false;
|
|
1196
1235
|
}
|
|
1197
1236
|
}
|
|
@@ -1202,8 +1241,8 @@ async function loadIndexSnapshotOrEmpty(phrenPath, profile) {
|
|
|
1202
1241
|
userSuffix = String(os.userInfo().uid);
|
|
1203
1242
|
}
|
|
1204
1243
|
catch (err) {
|
|
1205
|
-
if ((process.env.PHREN_DEBUG
|
|
1206
|
-
process.stderr.write(`[phren] loadIndexSnapshotOrEmpty userInfo: ${
|
|
1244
|
+
if ((process.env.PHREN_DEBUG))
|
|
1245
|
+
process.stderr.write(`[phren] loadIndexSnapshotOrEmpty userInfo: ${errorMessage(err)}\n`);
|
|
1207
1246
|
userSuffix = crypto.createHash("sha1").update(homeDir()).digest("hex").slice(0, 12);
|
|
1208
1247
|
}
|
|
1209
1248
|
const cacheDir = path.join(os.tmpdir(), `phren-fts-${userSuffix}`);
|
|
@@ -1303,8 +1342,8 @@ export function findFtsCacheForPath(phrenPath, profile) {
|
|
|
1303
1342
|
userSuffix = String(os.userInfo().uid);
|
|
1304
1343
|
}
|
|
1305
1344
|
catch (err) {
|
|
1306
|
-
if ((process.env.PHREN_DEBUG
|
|
1307
|
-
process.stderr.write(`[phren] findFtsCacheForPath userInfo: ${
|
|
1345
|
+
if ((process.env.PHREN_DEBUG))
|
|
1346
|
+
process.stderr.write(`[phren] findFtsCacheForPath userInfo: ${errorMessage(err)}\n`);
|
|
1308
1347
|
userSuffix = crypto.createHash("sha1").update(homeDir()).digest("hex").slice(0, 12);
|
|
1309
1348
|
}
|
|
1310
1349
|
const cacheDir = path.join(os.tmpdir(), `phren-fts-${userSuffix}`);
|
|
@@ -1318,8 +1357,8 @@ export function findFtsCacheForPath(phrenPath, profile) {
|
|
|
1318
1357
|
}
|
|
1319
1358
|
}
|
|
1320
1359
|
catch (err) {
|
|
1321
|
-
if ((process.env.PHREN_DEBUG
|
|
1322
|
-
process.stderr.write(`[phren] findFtsCacheForPath: ${
|
|
1360
|
+
if ((process.env.PHREN_DEBUG))
|
|
1361
|
+
process.stderr.write(`[phren] findFtsCacheForPath: ${errorMessage(err)}\n`);
|
|
1323
1362
|
}
|
|
1324
1363
|
return { exists: false };
|
|
1325
1364
|
}
|