@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/cli-govern.js
CHANGED
|
@@ -144,7 +144,7 @@ export async function handlePruneMemories(args = []) {
|
|
|
144
144
|
.filter((e) => e !== null);
|
|
145
145
|
}
|
|
146
146
|
catch (err) {
|
|
147
|
-
if ((process.env.PHREN_DEBUG
|
|
147
|
+
if ((process.env.PHREN_DEBUG))
|
|
148
148
|
process.stderr.write(`[phren] cli-govern retrievalLog readParse: ${errorMessage(err)}\n`);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
@@ -255,7 +255,7 @@ export async function handleGcMaintain(args = []) {
|
|
|
255
255
|
const sevenDaysAgo = new Date(Date.now() - 7 * 86400000).toISOString().slice(0, 10);
|
|
256
256
|
let oldCommits = [];
|
|
257
257
|
try {
|
|
258
|
-
const raw = execSync(`git log --
|
|
258
|
+
const raw = execSync(`git log --before="${sevenDaysAgo}" --format="%H %ci %s"`, { cwd: phrenPath, encoding: "utf8" }).trim();
|
|
259
259
|
if (raw) {
|
|
260
260
|
oldCommits = raw.split("\n").filter((l) => l.includes("auto-save:") || l.includes("[auto]"));
|
|
261
261
|
}
|
|
@@ -271,23 +271,21 @@ export async function handleGcMaintain(args = []) {
|
|
|
271
271
|
report.commitsSquashed = oldCommits.length;
|
|
272
272
|
}
|
|
273
273
|
else {
|
|
274
|
-
// Group by ISO week
|
|
274
|
+
// Group by ISO week based on commit timestamp (already in the log output)
|
|
275
275
|
const commitsByWeek = new Map();
|
|
276
276
|
for (const line of oldCommits) {
|
|
277
277
|
const hash = line.split(" ")[0];
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
// Skip commits we can't resolve
|
|
290
|
-
}
|
|
278
|
+
// Format: "HASH YYYY-MM-DD HH:MM:SS +ZZZZ subject..."
|
|
279
|
+
const dateMatch = line.match(/^[a-f0-9]+ (\d{4}-\d{2}-\d{2})/);
|
|
280
|
+
if (!dateMatch)
|
|
281
|
+
continue;
|
|
282
|
+
const date = new Date(dateMatch[1]);
|
|
283
|
+
const weekStart = new Date(date);
|
|
284
|
+
weekStart.setDate(date.getDate() - date.getDay());
|
|
285
|
+
const weekKey = weekStart.toISOString().slice(0, 10);
|
|
286
|
+
if (!commitsByWeek.has(weekKey))
|
|
287
|
+
commitsByWeek.set(weekKey, []);
|
|
288
|
+
commitsByWeek.get(weekKey).push(hash);
|
|
291
289
|
}
|
|
292
290
|
// For each week with multiple commits, soft-reset to oldest and amend into a summary
|
|
293
291
|
for (const [weekKey, hashes] of commitsByWeek.entries()) {
|
|
@@ -320,64 +318,38 @@ export async function handleGcMaintain(args = []) {
|
|
|
320
318
|
console.log("Commit squash: all old auto-save weeks have only one commit, nothing to squash.");
|
|
321
319
|
}
|
|
322
320
|
}
|
|
323
|
-
// 3. Prune stale
|
|
324
|
-
const sessionsDir = path.join(phrenPath, ".sessions");
|
|
321
|
+
// 3–4. Prune stale files from .sessions/ and .runtime/
|
|
325
322
|
const thirtyDaysAgo = Date.now() - 30 * 86400000;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (stat.mtimeMs < thirtyDaysAgo) {
|
|
333
|
-
if (dryRun) {
|
|
334
|
-
console.log(`[dry-run] Would remove session marker: .sessions/${entry}`);
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
fs.unlinkSync(fullPath);
|
|
338
|
-
}
|
|
339
|
-
report.sessionsRemoved++;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
catch {
|
|
343
|
-
// Skip unreadable entries
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
const sessionsVerb = dryRun ? "Would remove" : "Removed";
|
|
348
|
-
console.log(`${sessionsVerb} ${report.sessionsRemoved} stale session marker(s) from .sessions/`);
|
|
349
|
-
// 4. Trim runtime logs from ~/.phren/.runtime/ older than 30 days
|
|
350
|
-
const runtimeDir = path.join(phrenPath, ".runtime");
|
|
351
|
-
const logExtensions = new Set([".log", ".jsonl", ".json"]);
|
|
352
|
-
if (fs.existsSync(runtimeDir)) {
|
|
353
|
-
const entries = fs.readdirSync(runtimeDir);
|
|
354
|
-
for (const entry of entries) {
|
|
355
|
-
const ext = path.extname(entry);
|
|
356
|
-
if (!logExtensions.has(ext))
|
|
357
|
-
continue;
|
|
358
|
-
// Never trim the active audit log or telemetry config
|
|
359
|
-
if (entry === "audit.log" || entry === "telemetry.json")
|
|
323
|
+
function pruneStaleFiles(dir, label, filter) {
|
|
324
|
+
let removed = 0;
|
|
325
|
+
if (!fs.existsSync(dir))
|
|
326
|
+
return removed;
|
|
327
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
328
|
+
if (filter && !filter(entry))
|
|
360
329
|
continue;
|
|
361
|
-
const fullPath = path.join(
|
|
330
|
+
const fullPath = path.join(dir, entry);
|
|
362
331
|
try {
|
|
363
|
-
|
|
364
|
-
if (stat.mtimeMs < thirtyDaysAgo) {
|
|
332
|
+
if (fs.statSync(fullPath).mtimeMs < thirtyDaysAgo) {
|
|
365
333
|
if (dryRun) {
|
|
366
|
-
console.log(`[dry-run] Would remove
|
|
334
|
+
console.log(`[dry-run] Would remove: ${label}/${entry}`);
|
|
367
335
|
}
|
|
368
336
|
else {
|
|
369
337
|
fs.unlinkSync(fullPath);
|
|
370
338
|
}
|
|
371
|
-
|
|
339
|
+
removed++;
|
|
372
340
|
}
|
|
373
341
|
}
|
|
374
|
-
catch {
|
|
375
|
-
// Skip unreadable entries
|
|
376
|
-
}
|
|
342
|
+
catch { /* skip unreadable */ }
|
|
377
343
|
}
|
|
344
|
+
return removed;
|
|
378
345
|
}
|
|
379
|
-
const
|
|
380
|
-
|
|
346
|
+
const logExtensions = new Set([".log", ".jsonl", ".json"]);
|
|
347
|
+
const protectedFiles = new Set(["audit.log", "telemetry.json"]);
|
|
348
|
+
report.sessionsRemoved = pruneStaleFiles(path.join(phrenPath, ".sessions"), ".sessions");
|
|
349
|
+
report.runtimeLogsRemoved = pruneStaleFiles(path.join(phrenPath, ".runtime"), ".runtime", (entry) => logExtensions.has(path.extname(entry)) && !protectedFiles.has(entry));
|
|
350
|
+
const verb = dryRun ? "Would remove" : "Removed";
|
|
351
|
+
console.log(`${verb} ${report.sessionsRemoved} stale session marker(s) from .sessions/`);
|
|
352
|
+
console.log(`${verb} ${report.runtimeLogsRemoved} stale runtime log(s) from .runtime/`);
|
|
381
353
|
// 5. Summary
|
|
382
354
|
if (!dryRun) {
|
|
383
355
|
appendAuditLog(phrenPath, "maintain_gc", `gitGc=${report.gitGcRan} squashed=${report.commitsSquashed} sessions=${report.sessionsRemoved} logs=${report.runtimeLogsRemoved}`);
|
|
@@ -529,7 +501,7 @@ export async function handleBackgroundMaintenance(projectArg) {
|
|
|
529
501
|
fs.unlinkSync(markers.lock);
|
|
530
502
|
}
|
|
531
503
|
catch (err) {
|
|
532
|
-
if ((process.env.PHREN_DEBUG
|
|
504
|
+
if ((process.env.PHREN_DEBUG))
|
|
533
505
|
process.stderr.write(`[phren] cli-govern backgroundMaintenance unlockFinal: ${errorMessage(err)}\n`);
|
|
534
506
|
}
|
|
535
507
|
}
|
package/mcp/dist/cli-graph.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getPhrenPath } from "./shared.js";
|
|
2
2
|
import { buildIndex, queryRows } from "./shared-index.js";
|
|
3
3
|
import { resolveRuntimeProfile } from "./runtime-profile.js";
|
|
4
|
+
import { errorMessage } from "./utils.js";
|
|
4
5
|
/**
|
|
5
6
|
* CLI: phren graph [--project <name>] [--limit <n>]
|
|
6
7
|
* Displays the fragment knowledge graph as a table.
|
|
@@ -115,7 +116,7 @@ export async function handleGraphLink(args) {
|
|
|
115
116
|
db.run("INSERT OR IGNORE INTO entity_links (source_id, target_id, rel_type, source_doc) VALUES (?, ?, ?, ?)", [sourceId, targetId, "mentions", sourceDoc]);
|
|
116
117
|
}
|
|
117
118
|
catch (err) {
|
|
118
|
-
console.error(`Failed to link: ${
|
|
119
|
+
console.error(`Failed to link: ${errorMessage(err)}`);
|
|
119
120
|
process.exit(1);
|
|
120
121
|
}
|
|
121
122
|
// Persist to manual-links.json
|
|
@@ -144,7 +145,7 @@ export async function handleGraphLink(args) {
|
|
|
144
145
|
});
|
|
145
146
|
}
|
|
146
147
|
catch (err) {
|
|
147
|
-
console.error(`Failed to persist manual link: ${
|
|
148
|
+
console.error(`Failed to persist manual link: ${errorMessage(err)}`);
|
|
148
149
|
process.exit(1);
|
|
149
150
|
}
|
|
150
151
|
console.log(`Linked "${fragmentName}" to ${sourceDoc}.`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { capCache } from "./shared.js";
|
|
4
|
+
import { errorMessage } from "./utils.js";
|
|
4
5
|
// ── Glob matching and project frontmatter ────────────────────────────────────
|
|
5
6
|
const projectGlobCache = new Map();
|
|
6
7
|
export function clearProjectGlobCache() {
|
|
@@ -45,7 +46,7 @@ function parseProjectGlobs(phrenPathLocal, project) {
|
|
|
45
46
|
}
|
|
46
47
|
catch (err) {
|
|
47
48
|
if (process.env.PHREN_DEBUG)
|
|
48
|
-
process.stderr.write(`[phren] getProjectGlobs: ${
|
|
49
|
+
process.stderr.write(`[phren] getProjectGlobs: ${errorMessage(err)}\n`);
|
|
49
50
|
}
|
|
50
51
|
projectGlobCache.set(project, globs);
|
|
51
52
|
capCache(projectGlobCache);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { recordInjection, recordRetrieval, } from "./shared-governance.js";
|
|
2
2
|
import { getDocSourceKey, } from "./shared-index.js";
|
|
3
3
|
import { logImpact, extractFindingIdsFromSnippet, } from "./finding-impact.js";
|
|
4
|
-
import { isFeatureEnabled } from "./utils.js";
|
|
4
|
+
import { isFeatureEnabled, errorMessage } from "./utils.js";
|
|
5
5
|
import { annotateStale } from "./cli-hooks-citations.js";
|
|
6
6
|
import { approximateTokens, fileRelevanceBoost, branchMatchBoost } from "./shared-retrieval.js";
|
|
7
7
|
// ── Progressive disclosure helpers ────────────────────────────────────────────
|
|
@@ -56,7 +56,7 @@ export function buildHookOutput(selected, usedTokens, intent, gitCtx, detectedPr
|
|
|
56
56
|
}
|
|
57
57
|
catch (err) {
|
|
58
58
|
if (process.env.PHREN_DEBUG)
|
|
59
|
-
process.stderr.write(`[phren] injectContext recordRetrieval: ${
|
|
59
|
+
process.stderr.write(`[phren] injectContext recordRetrieval: ${errorMessage(err)}\n`);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -108,7 +108,7 @@ export function buildHookOutput(selected, usedTokens, intent, gitCtx, detectedPr
|
|
|
108
108
|
}
|
|
109
109
|
catch (err) {
|
|
110
110
|
if (process.env.PHREN_DEBUG)
|
|
111
|
-
process.stderr.write(`[phren] injectContext recordRetrievalOrdered: ${
|
|
111
|
+
process.stderr.write(`[phren] injectContext recordRetrievalOrdered: ${errorMessage(err)}\n`);
|
|
112
112
|
}
|
|
113
113
|
parts.push(`[${getDocSourceKey(doc, phrenPathLocal)}] (${doc.type})`);
|
|
114
114
|
parts.push(annotateStale(snippet));
|
package/mcp/dist/cli-hooks.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// cli-hooks-output.ts — hook output formatting
|
|
6
6
|
// cli-hooks-globs.ts — project glob matching
|
|
7
7
|
import { debugLog, sessionMarker, sessionsDir, getPhrenPath, } from "./shared.js";
|
|
8
|
-
import {
|
|
8
|
+
import { mergeConfig, } from "./shared-governance.js";
|
|
9
9
|
import { buildIndex, detectProject, } from "./shared-index.js";
|
|
10
10
|
import { isProjectHookEnabled } from "./project-config.js";
|
|
11
11
|
import { checkConsolidationNeeded, } from "./shared-content.js";
|
|
@@ -126,7 +126,7 @@ export async function handleHookPrompt() {
|
|
|
126
126
|
}
|
|
127
127
|
catch (err) {
|
|
128
128
|
if (process.env.PHREN_DEBUG)
|
|
129
|
-
process.stderr.write(`[phren] hookPrompt stdinRead: ${
|
|
129
|
+
process.stderr.write(`[phren] hookPrompt stdinRead: ${errorMessage(err)}\n`);
|
|
130
130
|
process.exit(0);
|
|
131
131
|
}
|
|
132
132
|
const input = parseHookInput(raw);
|
|
@@ -182,6 +182,7 @@ export async function handleHookPrompt() {
|
|
|
182
182
|
appendAuditLog(getPhrenPath(), "hook_prompt", `status=project_disabled project=${detectedProject}`);
|
|
183
183
|
process.exit(0);
|
|
184
184
|
}
|
|
185
|
+
const resolvedConfig = mergeConfig(getPhrenPath(), detectedProject ?? undefined);
|
|
185
186
|
const safeQuery = buildRobustFtsQuery(keywords, detectedProject, getPhrenPath());
|
|
186
187
|
if (!safeQuery)
|
|
187
188
|
process.exit(0);
|
|
@@ -193,14 +194,17 @@ export async function handleHookPrompt() {
|
|
|
193
194
|
process.exit(0);
|
|
194
195
|
autoLearnQuerySynonyms(getPhrenPath(), detectedProject, keywordEntries, rows);
|
|
195
196
|
const tTrust0 = Date.now();
|
|
196
|
-
const policy =
|
|
197
|
+
const policy = resolvedConfig.retentionPolicy;
|
|
197
198
|
const memoryTtlDays = Number.parseInt(process.env.PHREN_MEMORY_TTL_DAYS || String(policy.ttlDays), 10);
|
|
198
199
|
const trustResult = applyTrustFilter(rows, Number.isNaN(memoryTtlDays) ? policy.ttlDays : memoryTtlDays, policy.minInjectConfidence, policy.decay, getPhrenPath());
|
|
199
200
|
rows = trustResult.rows;
|
|
200
201
|
stage.trustMs = Date.now() - tTrust0;
|
|
201
202
|
if (!rows.length)
|
|
202
203
|
process.exit(0);
|
|
203
|
-
|
|
204
|
+
const findingsProactivity = resolvedConfig.proactivity.findings
|
|
205
|
+
?? resolvedConfig.proactivity.base
|
|
206
|
+
?? getProactivityLevelForFindings(getPhrenPath());
|
|
207
|
+
if (isFeatureEnabled("PHREN_FEATURE_AUTO_EXTRACT", true) && findingsProactivity !== "low" && sessionId && detectedProject && cwd) {
|
|
204
208
|
const marker = sessionMarker(getPhrenPath(), `extracted-${sessionId}-${detectedProject}`);
|
|
205
209
|
if (!fs.existsSync(marker)) {
|
|
206
210
|
try {
|
|
@@ -260,7 +264,9 @@ export async function handleHookPrompt() {
|
|
|
260
264
|
debugLog(`injection-budget: trimmed ${selected.length} -> ${kept.length} snippets to fit ${maxInjectTokens} token budget`);
|
|
261
265
|
}
|
|
262
266
|
const parts = buildHookOutput(budgetSelected, budgetUsedTokens, intent, gitCtx, detectedProject, stage, safeTokenBudget, getPhrenPath(), sessionId);
|
|
263
|
-
const taskLevel =
|
|
267
|
+
const taskLevel = resolvedConfig.proactivity.tasks
|
|
268
|
+
?? resolvedConfig.proactivity.base
|
|
269
|
+
?? getProactivityLevelForTask(getPhrenPath());
|
|
264
270
|
const taskLifecycle = handleTaskPromptLifecycle({
|
|
265
271
|
phrenPath: getPhrenPath(),
|
|
266
272
|
prompt,
|
|
@@ -275,8 +281,7 @@ export async function handleHookPrompt() {
|
|
|
275
281
|
}
|
|
276
282
|
// Inject finding sensitivity agent instruction
|
|
277
283
|
try {
|
|
278
|
-
const
|
|
279
|
-
const sensitivity = workflowPolicy.findingSensitivity ?? "balanced";
|
|
284
|
+
const sensitivity = resolvedConfig.findingSensitivity ?? "balanced";
|
|
280
285
|
const sensitivityConfig = FINDING_SENSITIVITY_CONFIG[sensitivity];
|
|
281
286
|
if (sensitivityConfig) {
|
|
282
287
|
parts.push("");
|
|
@@ -304,36 +309,38 @@ export async function handleHookPrompt() {
|
|
|
304
309
|
const noticeFile = sessionId ? sessionMarker(getPhrenPath(), `noticed-${sessionId}`) : null;
|
|
305
310
|
const alreadyNoticed = noticeFile ? fs.existsSync(noticeFile) : false;
|
|
306
311
|
if (!alreadyNoticed) {
|
|
307
|
-
//
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
312
|
+
// Defer stale session marker cleanup to after output — it's I/O-heavy and not needed for results
|
|
313
|
+
setImmediate(() => {
|
|
314
|
+
try {
|
|
315
|
+
const cutoff = Date.now() - 86400000;
|
|
316
|
+
const sessDir = sessionsDir(getPhrenPath());
|
|
317
|
+
if (fs.existsSync(sessDir)) {
|
|
318
|
+
for (const f of fs.readdirSync(sessDir)) {
|
|
319
|
+
if (!f.startsWith("noticed-") && !f.startsWith("extracted-"))
|
|
320
|
+
continue;
|
|
321
|
+
const fp = `${sessDir}/${f}`;
|
|
322
|
+
try {
|
|
323
|
+
if (fs.statSync(fp).mtimeMs < cutoff)
|
|
324
|
+
fs.unlinkSync(fp);
|
|
325
|
+
}
|
|
326
|
+
catch { /* ignore per-file errors */ }
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// Also clean stale markers from the phren root
|
|
330
|
+
for (const f of fs.readdirSync(getPhrenPath())) {
|
|
331
|
+
if (!f.startsWith(".noticed-") && !f.startsWith(".extracted-"))
|
|
314
332
|
continue;
|
|
315
|
-
const fp = `${
|
|
316
|
-
|
|
333
|
+
const fp = `${getPhrenPath()}/${f}`;
|
|
334
|
+
try {
|
|
317
335
|
fs.unlinkSync(fp);
|
|
336
|
+
}
|
|
337
|
+
catch { /* ignore */ }
|
|
318
338
|
}
|
|
319
339
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (!f.startsWith(".noticed-") && !f.startsWith(".extracted-"))
|
|
323
|
-
continue;
|
|
324
|
-
const fp = `${getPhrenPath()}/${f}`;
|
|
325
|
-
try {
|
|
326
|
-
fs.unlinkSync(fp);
|
|
327
|
-
}
|
|
328
|
-
catch (err) {
|
|
329
|
-
if (process.env.PHREN_DEBUG)
|
|
330
|
-
process.stderr.write(`[phren] hookPrompt staleNoticeUnlink: ${errorMessage(err)}\n`);
|
|
331
|
-
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
debugLog(`stale notice cleanup failed: ${errorMessage(err)}`);
|
|
332
342
|
}
|
|
333
|
-
}
|
|
334
|
-
catch (err) {
|
|
335
|
-
debugLog(`stale notice cleanup failed: ${errorMessage(err)}`);
|
|
336
|
-
}
|
|
343
|
+
});
|
|
337
344
|
const needed = checkConsolidationNeeded(getPhrenPath(), profile);
|
|
338
345
|
if (needed.length > 0) {
|
|
339
346
|
const notices = needed.map((n) => {
|
|
@@ -5,6 +5,7 @@ import { expandHomePath, findProjectNameCaseInsensitive, getPhrenPath, getProjec
|
|
|
5
5
|
import { isValidProjectName, errorMessage } from "./utils.js";
|
|
6
6
|
import { readInstallPreferences, writeInstallPreferences } from "./init-preferences.js";
|
|
7
7
|
import { buildSkillManifest, findLocalSkill, findSkill, getAllSkills } from "./skill-registry.js";
|
|
8
|
+
import { detectSkillCollisions } from "./link-skills.js";
|
|
8
9
|
import { setSkillEnabledAndSync, syncSkillLinksForScope } from "./skill-files.js";
|
|
9
10
|
import { findProjectDir } from "./project-locator.js";
|
|
10
11
|
import { TASK_FILE_ALIASES, addTask, completeTask, updateTask, reorderTask, pinTask, removeTask, workNextTask, tidyDoneTasks, linkTaskIssue, promoteTask, resolveTaskItem } from "./data-tasks.js";
|
|
@@ -76,7 +77,7 @@ function openInEditor(filePath) {
|
|
|
76
77
|
execFileSync(editor, [filePath], { stdio: "inherit" });
|
|
77
78
|
}
|
|
78
79
|
catch (err) {
|
|
79
|
-
if ((process.env.PHREN_DEBUG
|
|
80
|
+
if ((process.env.PHREN_DEBUG))
|
|
80
81
|
process.stderr.write(`[phren] openInEditor: ${errorMessage(err)}\n`);
|
|
81
82
|
console.error(`Editor "${editor}" failed. Set $EDITOR to your preferred editor.`);
|
|
82
83
|
process.exit(1);
|
|
@@ -145,7 +146,7 @@ export function handleSkillsNamespace(args, profile) {
|
|
|
145
146
|
console.log(`Linked skill ${fileName} into ${project}.`);
|
|
146
147
|
}
|
|
147
148
|
catch (err) {
|
|
148
|
-
if ((process.env.PHREN_DEBUG
|
|
149
|
+
if ((process.env.PHREN_DEBUG))
|
|
149
150
|
process.stderr.write(`[phren] skill add symlinkFailed: ${errorMessage(err)}\n`);
|
|
150
151
|
fs.copyFileSync(source, dest);
|
|
151
152
|
console.log(`Copied skill ${fileName} into ${project}.`);
|
|
@@ -450,7 +451,7 @@ export function handleDetectSkills(args, profile) {
|
|
|
450
451
|
continue;
|
|
451
452
|
}
|
|
452
453
|
catch (err) {
|
|
453
|
-
if ((process.env.PHREN_DEBUG
|
|
454
|
+
if ((process.env.PHREN_DEBUG))
|
|
454
455
|
process.stderr.write(`[phren] skillList lstat: ${errorMessage(err)}\n`);
|
|
455
456
|
}
|
|
456
457
|
const name = entry.replace(/\.md$/, "");
|
|
@@ -548,6 +549,15 @@ function printSkillDoctor(scope, manifest, destDir) {
|
|
|
548
549
|
problems.push(`Mirror drift for ${skill.name}: expected ${dest} -> ${skill.root}`);
|
|
549
550
|
}
|
|
550
551
|
}
|
|
552
|
+
// Check for user-owned files blocking phren skill links
|
|
553
|
+
const phrenPath = getPhrenPath();
|
|
554
|
+
const srcDir = scope.toLowerCase() === "global"
|
|
555
|
+
? path.join(phrenPath, "global", "skills")
|
|
556
|
+
: path.join(phrenPath, scope, "skills");
|
|
557
|
+
const collisions = detectSkillCollisions(srcDir, destDir, phrenPath);
|
|
558
|
+
for (const collision of collisions) {
|
|
559
|
+
problems.push(`Skill collision: ${collision.message}`);
|
|
560
|
+
}
|
|
551
561
|
}
|
|
552
562
|
if (!manifest.problems.length && !problems.length) {
|
|
553
563
|
console.log("\nDoctor: no skill pipeline issues detected.");
|
|
@@ -853,7 +863,7 @@ function handleProjectsList(profile) {
|
|
|
853
863
|
dirFiles = new Set(fs.readdirSync(projectDir));
|
|
854
864
|
}
|
|
855
865
|
catch (err) {
|
|
856
|
-
if ((process.env.PHREN_DEBUG
|
|
866
|
+
if ((process.env.PHREN_DEBUG))
|
|
857
867
|
process.stderr.write(`[phren] projects list readdir: ${errorMessage(err)}\n`);
|
|
858
868
|
dirFiles = new Set();
|
|
859
869
|
}
|
|
@@ -896,7 +906,7 @@ async function handleProjectsRemove(name, profile) {
|
|
|
896
906
|
countFiles(projectDir);
|
|
897
907
|
}
|
|
898
908
|
catch (err) {
|
|
899
|
-
if ((process.env.PHREN_DEBUG
|
|
909
|
+
if ((process.env.PHREN_DEBUG))
|
|
900
910
|
process.stderr.write(`[phren] projects remove countFiles: ${errorMessage(err)}\n`);
|
|
901
911
|
}
|
|
902
912
|
const readline = await import("readline");
|
package/mcp/dist/cli-search.js
CHANGED
|
@@ -34,7 +34,7 @@ export function readSearchHistory(phrenPath) {
|
|
|
34
34
|
.map((line) => JSON.parse(line));
|
|
35
35
|
}
|
|
36
36
|
catch (err) {
|
|
37
|
-
if ((process.env.PHREN_DEBUG
|
|
37
|
+
if ((process.env.PHREN_DEBUG))
|
|
38
38
|
process.stderr.write(`[phren] readSearchHistory: ${errorMessage(err)}\n`);
|
|
39
39
|
return [];
|
|
40
40
|
}
|
|
@@ -253,7 +253,7 @@ export async function runSearch(opts, phrenPath, profile) {
|
|
|
253
253
|
logSearchMiss(phrenPath, opts.query, opts.project);
|
|
254
254
|
}
|
|
255
255
|
catch (err) {
|
|
256
|
-
if ((process.env.PHREN_DEBUG
|
|
256
|
+
if ((process.env.PHREN_DEBUG))
|
|
257
257
|
process.stderr.write(`[phren] search logSearchMiss: ${errorMessage(err)}\n`);
|
|
258
258
|
}
|
|
259
259
|
}
|
|
@@ -107,7 +107,7 @@ function isAlreadyArchived(referenceDir, bullet) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
catch (err) {
|
|
110
|
-
if ((process.env.PHREN_DEBUG
|
|
110
|
+
if ((process.env.PHREN_DEBUG))
|
|
111
111
|
process.stderr.write(`[phren] isDuplicateInReference: ${errorMessage(err)}\n`);
|
|
112
112
|
}
|
|
113
113
|
return false;
|
|
@@ -271,7 +271,7 @@ export function autoArchiveToReference(phrenPath, project, keepCount) {
|
|
|
271
271
|
fs.unlinkSync(lockFile);
|
|
272
272
|
}
|
|
273
273
|
catch (err) {
|
|
274
|
-
if ((process.env.PHREN_DEBUG
|
|
274
|
+
if ((process.env.PHREN_DEBUG))
|
|
275
275
|
process.stderr.write(`[phren] autoArchiveToReference unlockFile: ${errorMessage(err)}\n`);
|
|
276
276
|
}
|
|
277
277
|
}
|
|
@@ -2,7 +2,7 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import * as crypto from "crypto";
|
|
4
4
|
import { debugLog, runtimeFile, KNOWN_OBSERVATION_TAGS } from "./shared.js";
|
|
5
|
-
import { isFeatureEnabled, safeProjectPath } from "./utils.js";
|
|
5
|
+
import { isFeatureEnabled, safeProjectPath, errorMessage } from "./utils.js";
|
|
6
6
|
import { UNIVERSAL_TECH_TERMS_RE, EXTRA_ENTITY_PATTERNS } from "./phren-core.js";
|
|
7
7
|
import { isInactiveFindingLine } from "./finding-lifecycle.js";
|
|
8
8
|
// ── LLM provider abstraction ────────────────────────────────────────────────
|
|
@@ -50,8 +50,8 @@ async function withCache(cachePath, key, ttlMs, compute) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
catch (err) {
|
|
53
|
-
if ((process.env.PHREN_DEBUG
|
|
54
|
-
process.stderr.write(`[phren] withCache load (${path.basename(cachePath)}): ${
|
|
53
|
+
if ((process.env.PHREN_DEBUG))
|
|
54
|
+
process.stderr.write(`[phren] withCache load (${path.basename(cachePath)}): ${errorMessage(err)}\n`);
|
|
55
55
|
}
|
|
56
56
|
const result = await compute();
|
|
57
57
|
// Persist result
|
|
@@ -61,8 +61,8 @@ async function withCache(cachePath, key, ttlMs, compute) {
|
|
|
61
61
|
persistCache(cachePath, cache);
|
|
62
62
|
}
|
|
63
63
|
catch (err) {
|
|
64
|
-
if ((process.env.PHREN_DEBUG
|
|
65
|
-
process.stderr.write(`[phren] withCache persist (${path.basename(cachePath)}): ${
|
|
64
|
+
if ((process.env.PHREN_DEBUG))
|
|
65
|
+
process.stderr.write(`[phren] withCache persist (${path.basename(cachePath)}): ${errorMessage(err)}\n`);
|
|
66
66
|
}
|
|
67
67
|
return result;
|
|
68
68
|
}
|
|
@@ -562,8 +562,8 @@ export async function checkSemanticConflicts(phrenPath, project, newFinding, sig
|
|
|
562
562
|
return { name: e.name, mtime: fs.statSync(fp).mtimeMs, fp };
|
|
563
563
|
}
|
|
564
564
|
catch (err) {
|
|
565
|
-
if ((process.env.PHREN_DEBUG
|
|
566
|
-
process.stderr.write(`[phren] crossProjectScan stat: ${
|
|
565
|
+
if ((process.env.PHREN_DEBUG))
|
|
566
|
+
process.stderr.write(`[phren] crossProjectScan stat: ${errorMessage(err)}\n`);
|
|
567
567
|
return null;
|
|
568
568
|
}
|
|
569
569
|
})
|
|
@@ -577,8 +577,8 @@ export async function checkSemanticConflicts(phrenPath, project, newFinding, sig
|
|
|
577
577
|
}
|
|
578
578
|
}
|
|
579
579
|
catch (err) {
|
|
580
|
-
if ((process.env.PHREN_DEBUG
|
|
581
|
-
process.stderr.write(`[phren] crossProjectScan: ${
|
|
580
|
+
if ((process.env.PHREN_DEBUG))
|
|
581
|
+
process.stderr.write(`[phren] crossProjectScan: ${errorMessage(err)}\n`);
|
|
582
582
|
}
|
|
583
583
|
const annotations = [];
|
|
584
584
|
const deadline = Date.now() + CONFLICT_CHECK_TOTAL_TIMEOUT_MS;
|
package/mcp/dist/embedding.js
CHANGED
|
@@ -79,7 +79,7 @@ async function openCacheDb(phrenPath) {
|
|
|
79
79
|
db?.close();
|
|
80
80
|
}
|
|
81
81
|
catch (e2) {
|
|
82
|
-
if ((process.env.PHREN_DEBUG
|
|
82
|
+
if ((process.env.PHREN_DEBUG))
|
|
83
83
|
process.stderr.write(`[phren] embedding openCacheDb dbClose: ${e2 instanceof Error ? e2.message : String(e2)}\n`);
|
|
84
84
|
}
|
|
85
85
|
throw err;
|
|
@@ -126,13 +126,13 @@ function persistDb(phrenPath, db) {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
catch (err) {
|
|
129
|
-
if ((process.env.PHREN_DEBUG
|
|
130
|
-
process.stderr.write(`[phren] embedding persistDb onDiskLoad: ${
|
|
129
|
+
if ((process.env.PHREN_DEBUG))
|
|
130
|
+
process.stderr.write(`[phren] embedding persistDb onDiskLoad: ${errorMessage(err)}\n`);
|
|
131
131
|
try {
|
|
132
132
|
onDisk?.close();
|
|
133
133
|
}
|
|
134
134
|
catch (e2) {
|
|
135
|
-
if ((process.env.PHREN_DEBUG
|
|
135
|
+
if ((process.env.PHREN_DEBUG))
|
|
136
136
|
process.stderr.write(`[phren] embedding persistDb onDiskClose: ${e2 instanceof Error ? e2.message : String(e2)}\n`);
|
|
137
137
|
}
|
|
138
138
|
onDisk = null;
|
|
@@ -149,7 +149,7 @@ function persistDb(phrenPath, db) {
|
|
|
149
149
|
onDisk.close();
|
|
150
150
|
}
|
|
151
151
|
catch (e2) {
|
|
152
|
-
if ((process.env.PHREN_DEBUG
|
|
152
|
+
if ((process.env.PHREN_DEBUG))
|
|
153
153
|
process.stderr.write(`[phren] embedding persistDb onDiskCloseFinally: ${e2 instanceof Error ? e2.message : String(e2)}\n`);
|
|
154
154
|
}
|
|
155
155
|
}
|
|
@@ -269,7 +269,7 @@ export async function getCachedEmbedding(phrenPath, text, apiKey, model) {
|
|
|
269
269
|
db?.close();
|
|
270
270
|
}
|
|
271
271
|
catch (e2) {
|
|
272
|
-
if ((process.env.PHREN_DEBUG
|
|
272
|
+
if ((process.env.PHREN_DEBUG))
|
|
273
273
|
process.stderr.write(`[phren] embedding getCachedEmbedding dbClose: ${e2 instanceof Error ? e2.message : String(e2)}\n`);
|
|
274
274
|
}
|
|
275
275
|
}
|
|
@@ -320,7 +320,7 @@ export async function getCachedEmbeddings(phrenPath, texts, apiKey, model) {
|
|
|
320
320
|
db?.close();
|
|
321
321
|
}
|
|
322
322
|
catch (e2) {
|
|
323
|
-
if ((process.env.PHREN_DEBUG
|
|
323
|
+
if ((process.env.PHREN_DEBUG))
|
|
324
324
|
process.stderr.write(`[phren] embedding getCachedEmbeddings dbClose: ${e2 instanceof Error ? e2.message : String(e2)}\n`);
|
|
325
325
|
}
|
|
326
326
|
}
|