@phren/cli 0.0.9 → 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 +140 -3
- 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 +41 -34
- 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-citation.js +12 -22
- package/mcp/dist/content-dedup.js +9 -9
- package/mcp/dist/data-access.js +1 -1
- package/mcp/dist/data-tasks.js +23 -0
- 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/mcp-tasks.js +21 -1
- 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 +22 -56
- 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
|
@@ -3,8 +3,8 @@ import { getQualityMultiplier, entryScoreKey, } from "./shared-governance.js";
|
|
|
3
3
|
import { queryDocRows, queryRows, cosineFallback, extractSnippet, getDocSourceKey, getEntityBoostDocs, decodeFiniteNumber, rowToDocWithRowid, } from "./shared-index.js";
|
|
4
4
|
import { filterTrustedFindingsDetailed, } from "./shared-content.js";
|
|
5
5
|
import { parseCitationComment } from "./content-citation.js";
|
|
6
|
-
import { getHighImpactFindings
|
|
7
|
-
import { buildFtsQueryVariants, buildRelaxedFtsQuery, isFeatureEnabled, STOP_WORDS } from "./utils.js";
|
|
6
|
+
import { getHighImpactFindings } from "./finding-impact.js";
|
|
7
|
+
import { buildFtsQueryVariants, buildRelaxedFtsQuery, isFeatureEnabled, STOP_WORDS, errorMessage } from "./utils.js";
|
|
8
8
|
import * as fs from "fs";
|
|
9
9
|
import * as path from "path";
|
|
10
10
|
import { getProjectGlobBoost } from "./cli-hooks-globs.js";
|
|
@@ -34,23 +34,27 @@ const TASK_RESCUE_SCORE_MARGIN = 0.6;
|
|
|
34
34
|
/** Fraction of bullets that must be low-value before applying the low-value penalty. */
|
|
35
35
|
const LOW_VALUE_BULLET_FRACTION = 0.5;
|
|
36
36
|
// ── Intent and scoring helpers ───────────────────────────────────────────────
|
|
37
|
+
const INTENT_SKILL_CMD_RE = /(?:^|\s)\/(?!(?:home|usr|var|tmp|etc|opt|api|mnt)\b)[a-z][\w-]*\b/;
|
|
38
|
+
const INTENT_SKILL_KW_RE = /\bskill\b/;
|
|
39
|
+
const INTENT_DEBUG_RE = /(bug|error|fix|broken|regression|fail|stack trace)/;
|
|
40
|
+
const INTENT_REVIEW_RE = /(review|audit|pr|pull request|nit|refactor)/;
|
|
41
|
+
const INTENT_BUILD_RE = /(build|deploy|release|ci|workflow|pipeline|test)/;
|
|
42
|
+
const INTENT_DOCS_RE = /\b(doc|docs|readme|explain|guide|instructions?)\b/;
|
|
37
43
|
export function detectTaskIntent(prompt) {
|
|
38
44
|
const p = prompt.toLowerCase();
|
|
39
|
-
if (
|
|
45
|
+
if (INTENT_SKILL_CMD_RE.test(p) || INTENT_SKILL_KW_RE.test(p))
|
|
40
46
|
return "skill";
|
|
41
|
-
if (
|
|
47
|
+
if (INTENT_DEBUG_RE.test(p))
|
|
42
48
|
return "debug";
|
|
43
|
-
if (
|
|
49
|
+
if (INTENT_REVIEW_RE.test(p))
|
|
44
50
|
return "review";
|
|
45
|
-
if (
|
|
51
|
+
if (INTENT_BUILD_RE.test(p))
|
|
46
52
|
return "build";
|
|
47
|
-
if (
|
|
53
|
+
if (INTENT_DOCS_RE.test(p))
|
|
48
54
|
return "docs";
|
|
49
55
|
return "general";
|
|
50
56
|
}
|
|
51
57
|
function intentBoost(intent, docType) {
|
|
52
|
-
if (intent === "skill" && docType === "skill")
|
|
53
|
-
return 4;
|
|
54
58
|
if (intent === "debug" && (docType === "findings" || docType === "reference"))
|
|
55
59
|
return 3;
|
|
56
60
|
if (intent === "review" && (docType === "canonical" || docType === "changelog"))
|
|
@@ -347,23 +351,10 @@ export function searchDocuments(db, safeQuery, prompt, keywords, detectedProject
|
|
|
347
351
|
if (ftsDocs.length === 0 && relaxedQuery && relaxedQuery !== safeQuery) {
|
|
348
352
|
runScopedFtsQuery(relaxedQuery);
|
|
349
353
|
}
|
|
350
|
-
// Tier 1.5: Fragment graph expansion
|
|
351
|
-
const fragmentExpansionDocs = [];
|
|
352
|
-
const queryLower = (prompt + " " + keywords).toLowerCase();
|
|
353
|
-
const fragmentBoostDocKeys = getEntityBoostDocs(db, queryLower);
|
|
354
|
-
for (const docKey of fragmentBoostDocKeys) {
|
|
355
|
-
if (ftsSeenKeys.has(docKey))
|
|
356
|
-
continue;
|
|
357
|
-
const rows = queryDocRows(db, "SELECT project, filename, type, content, path FROM docs WHERE path = ? LIMIT 1", [docKey]);
|
|
358
|
-
if (rows?.length) {
|
|
359
|
-
ftsSeenKeys.add(docKey);
|
|
360
|
-
fragmentExpansionDocs.push(rows[0]);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
354
|
// Tier 2: Token-overlap semantic — always run, scored independently
|
|
364
355
|
const semanticDocs = semanticFallbackDocs(db, `${prompt}\n${keywords}`, detectedProject);
|
|
365
356
|
// Merge with Reciprocal Rank Fusion so documents found by both tiers rank highest
|
|
366
|
-
const merged = rrfMerge([ftsDocs,
|
|
357
|
+
const merged = rrfMerge([ftsDocs, semanticDocs]);
|
|
367
358
|
if (merged.length === 0)
|
|
368
359
|
return null;
|
|
369
360
|
return merged.slice(0, 12);
|
|
@@ -400,8 +391,8 @@ export async function searchDocumentsAsync(db, safeQuery, prompt, keywords, dete
|
|
|
400
391
|
}
|
|
401
392
|
catch (err) {
|
|
402
393
|
// Vector search failure is non-fatal — return sync result
|
|
403
|
-
if (process.env.PHREN_DEBUG)
|
|
404
|
-
process.stderr.write(`[phren] hybridSearch vectorFallback: ${
|
|
394
|
+
if ((process.env.PHREN_DEBUG))
|
|
395
|
+
process.stderr.write(`[phren] hybridSearch vectorFallback: ${errorMessage(err)}\n`);
|
|
405
396
|
return syncResult;
|
|
406
397
|
}
|
|
407
398
|
}
|
|
@@ -460,7 +451,7 @@ export async function searchKnowledgeRows(db, options) {
|
|
|
460
451
|
}
|
|
461
452
|
}
|
|
462
453
|
catch (err) {
|
|
463
|
-
debugLog(`rowid dedup query failed: ${
|
|
454
|
+
debugLog(`rowid dedup query failed: ${errorMessage(err)}`);
|
|
464
455
|
}
|
|
465
456
|
const cosineResults = cosineFallback(db, query, ftsRowids, maxResults - rows.length)
|
|
466
457
|
.filter((doc) => (!filterProject || doc.project === filterProject) && (!filterType || doc.type === filterType));
|
|
@@ -500,8 +491,8 @@ export async function searchKnowledgeRows(db, options) {
|
|
|
500
491
|
}
|
|
501
492
|
}
|
|
502
493
|
catch (err) {
|
|
503
|
-
if (process.env.PHREN_DEBUG) {
|
|
504
|
-
process.stderr.write(`[phren] vectorFallback: ${
|
|
494
|
+
if ((process.env.PHREN_DEBUG)) {
|
|
495
|
+
process.stderr.write(`[phren] vectorFallback: ${errorMessage(err)}\n`);
|
|
505
496
|
}
|
|
506
497
|
}
|
|
507
498
|
}
|
|
@@ -515,7 +506,6 @@ export function applyTrustFilter(rows, ttlDays, minConfidence, decay, phrenPath)
|
|
|
515
506
|
const queueItems = [];
|
|
516
507
|
const auditEntries = [];
|
|
517
508
|
const highImpactFindingIds = phrenPath ? getHighImpactFindings(phrenPath, 3) : undefined;
|
|
518
|
-
const impactCounts = phrenPath ? getImpactSurfaceCounts(phrenPath, 1) : undefined;
|
|
519
509
|
const filtered = rows
|
|
520
510
|
.map((doc) => {
|
|
521
511
|
if (!TRUST_FILTERED_TYPES.has(doc.type))
|
|
@@ -526,7 +516,6 @@ export function applyTrustFilter(rows, ttlDays, minConfidence, decay, phrenPath)
|
|
|
526
516
|
decay,
|
|
527
517
|
project: doc.project,
|
|
528
518
|
highImpactFindingIds,
|
|
529
|
-
impactCounts,
|
|
530
519
|
});
|
|
531
520
|
if (trust.issues.length > 0) {
|
|
532
521
|
const stale = trust.issues.filter((i) => i.reason === "stale").map((i) => i.bullet);
|
|
@@ -630,7 +619,7 @@ export function rankResults(rows, intent, gitCtx, detectedProject, phrenPathLoca
|
|
|
630
619
|
const scored = ranked.map((doc) => {
|
|
631
620
|
const globBoost = getProjectGlobBoost(phrenPathLocal, doc.project, cwd, gitCtx?.changedFiles);
|
|
632
621
|
const key = entryScoreKey(doc.project, doc.filename, doc.content);
|
|
633
|
-
const entity = entityBoostPaths.has(doc.path) ? 1.
|
|
622
|
+
const entity = entityBoostPaths.has(doc.path) ? 1.3 : 1;
|
|
634
623
|
const date = getRecentDate(doc);
|
|
635
624
|
const fileRel = fileRelevanceBoost(doc.path, changedFiles);
|
|
636
625
|
const branchMat = branchMatchBoost(doc.content, gitCtx?.branch);
|
|
@@ -645,12 +634,7 @@ export function rankResults(rows, intent, gitCtx, detectedProject, phrenPathLoca
|
|
|
645
634
|
&& queryOverlap < WEAK_CROSS_PROJECT_OVERLAP_MAX
|
|
646
635
|
? WEAK_CROSS_PROJECT_OVERLAP_PENALTY
|
|
647
636
|
: 0;
|
|
648
|
-
// Boost skills whose filename matches a query token (e.g. "swarm" matches swarm.md)
|
|
649
|
-
const skillNameBoost = doc.type === "skill" && queryTokens.length > 0
|
|
650
|
-
? queryTokens.some((t) => doc.filename.replace(/\.md$/i, "").toLowerCase() === t) ? 4 : 0
|
|
651
|
-
: 0;
|
|
652
637
|
const score = Math.round((intentBoost(intent, doc.type) +
|
|
653
|
-
skillNameBoost +
|
|
654
638
|
fileRel +
|
|
655
639
|
branchMat +
|
|
656
640
|
globBoost +
|
|
@@ -754,8 +738,8 @@ export function markStaleCitations(snippet) {
|
|
|
754
738
|
}
|
|
755
739
|
}
|
|
756
740
|
catch (err) {
|
|
757
|
-
if (process.env.PHREN_DEBUG)
|
|
758
|
-
process.stderr.write(`[phren] applyCitationAnnotations fileRead: ${
|
|
741
|
+
if ((process.env.PHREN_DEBUG))
|
|
742
|
+
process.stderr.write(`[phren] applyCitationAnnotations fileRead: ${errorMessage(err)}\n`);
|
|
759
743
|
stale = true;
|
|
760
744
|
}
|
|
761
745
|
}
|
|
@@ -771,23 +755,6 @@ export function markStaleCitations(snippet) {
|
|
|
771
755
|
}
|
|
772
756
|
return result.join("\n");
|
|
773
757
|
}
|
|
774
|
-
function annotateContradictions(snippet) {
|
|
775
|
-
return snippet.split('\n').map(line => {
|
|
776
|
-
const conflictMatch = line.match(/<!-- conflicts_with: "(.*?)" -->/);
|
|
777
|
-
const contradictMatch = line.match(/<!-- phren:contradicts "(.*?)" -->/);
|
|
778
|
-
const statusMatch = line.match(/phren:status "contradicted"/);
|
|
779
|
-
if (conflictMatch) {
|
|
780
|
-
return line.replace(conflictMatch[0], '') + ` [CONTRADICTED — conflicts with: "${conflictMatch[1]}"]`;
|
|
781
|
-
}
|
|
782
|
-
if (contradictMatch) {
|
|
783
|
-
return line.replace(contradictMatch[0], '') + ` [CONTRADICTED — see: "${contradictMatch[1]}"]`;
|
|
784
|
-
}
|
|
785
|
-
if (statusMatch) {
|
|
786
|
-
return line + ' [CONTRADICTED]';
|
|
787
|
-
}
|
|
788
|
-
return line;
|
|
789
|
-
}).join('\n');
|
|
790
|
-
}
|
|
791
758
|
export function selectSnippets(rows, keywords, tokenBudget, lineBudget, charBudget) {
|
|
792
759
|
const selected = [];
|
|
793
760
|
let usedTokens = 36;
|
|
@@ -813,7 +780,6 @@ export function selectSnippets(rows, keywords, tokenBudget, lineBudget, charBudg
|
|
|
813
780
|
if (TRUST_FILTERED_TYPES.has(doc.type)) {
|
|
814
781
|
snippet = markStaleCitations(snippet);
|
|
815
782
|
}
|
|
816
|
-
snippet = annotateContradictions(snippet);
|
|
817
783
|
snippet = dedupSnippetBullets(snippet);
|
|
818
784
|
if (!snippet.trim())
|
|
819
785
|
continue;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "crypto";
|
|
2
2
|
import { debugLog } from "./shared.js";
|
|
3
|
-
import { STOP_WORDS } from "./utils.js";
|
|
3
|
+
import { STOP_WORDS, errorMessage } from "./utils.js";
|
|
4
4
|
import { porterStem } from "./shared-stemmer.js";
|
|
5
5
|
import { classifyFile, normalizeIndexedContent, rowToDocWithRowid } from "./shared-index.js";
|
|
6
6
|
import { embedText, cosineSimilarity, getEmbeddingModel, getOllamaUrl, getCloudEmbeddingUrl } from "./shared-ollama.js";
|
|
@@ -191,8 +191,8 @@ export function cosineFallback(db, query, excludeRowids, limit) {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
catch (err) {
|
|
194
|
-
if ((process.env.PHREN_DEBUG
|
|
195
|
-
process.stderr.write(`[phren] cosineFallback count: ${
|
|
194
|
+
if ((process.env.PHREN_DEBUG))
|
|
195
|
+
process.stderr.write(`[phren] cosineFallback count: ${errorMessage(err)}\n`);
|
|
196
196
|
return [];
|
|
197
197
|
}
|
|
198
198
|
if (totalDocs > COSINE_MAX_CORPUS) {
|
|
@@ -220,8 +220,8 @@ export function cosineFallback(db, query, excludeRowids, limit) {
|
|
|
220
220
|
ftsRows.push(...ftsRes[0].values);
|
|
221
221
|
}
|
|
222
222
|
catch (err) {
|
|
223
|
-
if ((process.env.PHREN_DEBUG
|
|
224
|
-
process.stderr.write(`[phren] cosineFallback FTS pre-filter: ${
|
|
223
|
+
if ((process.env.PHREN_DEBUG))
|
|
224
|
+
process.stderr.write(`[phren] cosineFallback FTS pre-filter: ${errorMessage(err)}\n`);
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
// If FTS gave fewer than cap, supplement with deterministic rowid windows.
|
|
@@ -258,8 +258,8 @@ export function cosineFallback(db, query, excludeRowids, limit) {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
catch (err) {
|
|
261
|
-
if ((process.env.PHREN_DEBUG
|
|
262
|
-
process.stderr.write(`[phren] cosineFallback deterministicSample: ${
|
|
261
|
+
if ((process.env.PHREN_DEBUG))
|
|
262
|
+
process.stderr.write(`[phren] cosineFallback deterministicSample: ${errorMessage(err)}\n`);
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
if (ftsRows.length === 0)
|
|
@@ -269,8 +269,8 @@ export function cosineFallback(db, query, excludeRowids, limit) {
|
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
catch (err) {
|
|
272
|
-
if ((process.env.PHREN_DEBUG
|
|
273
|
-
process.stderr.write(`[phren] cosineFallback loadDocs: ${
|
|
272
|
+
if ((process.env.PHREN_DEBUG))
|
|
273
|
+
process.stderr.write(`[phren] cosineFallback loadDocs: ${errorMessage(err)}\n`);
|
|
274
274
|
return [];
|
|
275
275
|
}
|
|
276
276
|
// Separate rowids, DocRows, and content strings for scoring
|
|
@@ -315,8 +315,8 @@ export async function vectorFallback(phrenPath, query, excludePaths, limit, proj
|
|
|
315
315
|
await cache.load();
|
|
316
316
|
}
|
|
317
317
|
catch (err) {
|
|
318
|
-
if ((process.env.PHREN_DEBUG
|
|
319
|
-
process.stderr.write(`[phren] vectorFallback cacheLoad: ${
|
|
318
|
+
if ((process.env.PHREN_DEBUG))
|
|
319
|
+
process.stderr.write(`[phren] vectorFallback cacheLoad: ${errorMessage(err)}\n`);
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
if (cache.size() === 0)
|
|
@@ -367,8 +367,8 @@ export async function vectorFallback(phrenPath, query, excludePaths, limit, proj
|
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
369
|
catch (err) {
|
|
370
|
-
if ((process.env.PHREN_DEBUG
|
|
371
|
-
process.stderr.write(`[phren] vectorFallback fileRead: ${
|
|
370
|
+
if ((process.env.PHREN_DEBUG))
|
|
371
|
+
process.stderr.write(`[phren] vectorFallback fileRead: ${errorMessage(err)}\n`);
|
|
372
372
|
}
|
|
373
373
|
return { project: entryProject, filename, type, content, path: e.path };
|
|
374
374
|
});
|
package/mcp/dist/shared-sqljs.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import { createRequire } from "module";
|
|
5
|
+
import { errorMessage } from "./utils.js";
|
|
5
6
|
const require = createRequire(import.meta.url);
|
|
6
7
|
/**
|
|
7
8
|
* Locate the sql.js-fts5 WASM binary by require.resolve with path-probe fallback.
|
|
@@ -14,8 +15,8 @@ function findWasmBinary() {
|
|
|
14
15
|
return fs.readFileSync(resolved);
|
|
15
16
|
}
|
|
16
17
|
catch (err) {
|
|
17
|
-
if ((process.env.PHREN_DEBUG
|
|
18
|
-
process.stderr.write(`[phren] findWasmBinary requireResolve: ${
|
|
18
|
+
if ((process.env.PHREN_DEBUG))
|
|
19
|
+
process.stderr.write(`[phren] findWasmBinary requireResolve: ${errorMessage(err)}\n`);
|
|
19
20
|
// fall through to path probing
|
|
20
21
|
}
|
|
21
22
|
const __filename = fileURLToPath(import.meta.url);
|
package/mcp/dist/shared.js
CHANGED
|
@@ -50,7 +50,7 @@ export function appendAuditLog(phrenPath, event, details) {
|
|
|
50
50
|
break;
|
|
51
51
|
}
|
|
52
52
|
catch (err) {
|
|
53
|
-
if ((process.env.PHREN_DEBUG
|
|
53
|
+
if ((process.env.PHREN_DEBUG))
|
|
54
54
|
process.stderr.write(`[phren] appendAuditLog lockWrite: ${errorMessage(err)}\n`);
|
|
55
55
|
try {
|
|
56
56
|
const stat = fs.statSync(lockPath);
|
|
@@ -68,7 +68,7 @@ export function appendAuditLog(phrenPath, event, details) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
catch (statErr) {
|
|
71
|
-
if ((process.env.PHREN_DEBUG
|
|
71
|
+
if ((process.env.PHREN_DEBUG))
|
|
72
72
|
process.stderr.write(`[phren] appendAuditLog staleStat: ${errorMessage(statErr)}\n`);
|
|
73
73
|
}
|
|
74
74
|
Atomics.wait(waiter, 0, 0, pollMs);
|
|
@@ -97,7 +97,7 @@ export function appendAuditLog(phrenPath, event, details) {
|
|
|
97
97
|
fs.unlinkSync(lockPath);
|
|
98
98
|
}
|
|
99
99
|
catch (err) {
|
|
100
|
-
if ((process.env.PHREN_DEBUG
|
|
100
|
+
if ((process.env.PHREN_DEBUG))
|
|
101
101
|
process.stderr.write(`[phren] appendAuditLog unlock: ${errorMessage(err)}\n`);
|
|
102
102
|
}
|
|
103
103
|
}
|
package/mcp/dist/shell-input.js
CHANGED
|
@@ -379,7 +379,7 @@ export async function executePalette(host, input) {
|
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
381
|
catch (err) {
|
|
382
|
-
if ((process.env.PHREN_DEBUG
|
|
382
|
+
if ((process.env.PHREN_DEBUG))
|
|
383
383
|
process.stderr.write(`[phren] shell status gitStatus: ${errorMessage(err)}\n`);
|
|
384
384
|
}
|
|
385
385
|
const auditPathNew = runtimeFile(host.phrenPath, "audit.log");
|
|
@@ -45,7 +45,7 @@ export function loadShellState(phrenPath) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
catch (err) {
|
|
48
|
-
if ((process.env.PHREN_DEBUG
|
|
48
|
+
if ((process.env.PHREN_DEBUG))
|
|
49
49
|
process.stderr.write(`[phren] loadShellState parse: ${errorMessage(err)}\n`);
|
|
50
50
|
return fallback;
|
|
51
51
|
}
|
package/mcp/dist/shell-view.js
CHANGED
|
@@ -13,6 +13,7 @@ import { listMachines, listProfiles, } from "./data-access.js";
|
|
|
13
13
|
import { readInstallPreferences } from "./init-preferences.js";
|
|
14
14
|
import { isProjectHookEnabled, readProjectConfig } from "./project-config.js";
|
|
15
15
|
import { getScopedSkills } from "./skill-registry.js";
|
|
16
|
+
import { errorMessage } from "./utils.js";
|
|
16
17
|
// ── Tab bar ────────────────────────────────────────────────────────────────
|
|
17
18
|
function renderTabBar(state) {
|
|
18
19
|
const cols = renderWidth();
|
|
@@ -248,8 +249,8 @@ function parseSubsections(taskPath, project, cache) {
|
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
catch (err) {
|
|
251
|
-
if ((process.env.PHREN_DEBUG
|
|
252
|
-
process.stderr.write(`[phren] buildSubsectionMap: ${
|
|
252
|
+
if ((process.env.PHREN_DEBUG))
|
|
253
|
+
process.stderr.write(`[phren] buildSubsectionMap: ${errorMessage(err)}\n`);
|
|
253
254
|
}
|
|
254
255
|
const newCache = { project, map };
|
|
255
256
|
return { map, cache: newCache };
|
package/mcp/dist/shell.js
CHANGED
package/mcp/dist/skill-files.js
CHANGED
|
@@ -5,6 +5,7 @@ import { findProjectDir } from "./project-locator.js";
|
|
|
5
5
|
import { buildSkillManifest } from "./skill-registry.js";
|
|
6
6
|
import { setSkillEnabled } from "./skill-state.js";
|
|
7
7
|
import { errorMessage } from "./utils.js";
|
|
8
|
+
import { isManagedSymlink } from "./link-skills.js";
|
|
8
9
|
function normalizeSkillRemovalTarget(skillPath) {
|
|
9
10
|
if (!skillPath)
|
|
10
11
|
return skillPath;
|
|
@@ -19,10 +20,9 @@ function symlinkManagedSkill(src, dest, managedRoot) {
|
|
|
19
20
|
if (stat.isSymbolicLink()) {
|
|
20
21
|
const currentTarget = fs.readlinkSync(dest);
|
|
21
22
|
const resolvedTarget = path.resolve(path.dirname(dest), currentTarget);
|
|
22
|
-
const managedPrefix = path.resolve(managedRoot) + path.sep;
|
|
23
23
|
if (resolvedTarget === path.resolve(src))
|
|
24
24
|
return;
|
|
25
|
-
if (!
|
|
25
|
+
if (!isManagedSymlink(dest, managedRoot))
|
|
26
26
|
return;
|
|
27
27
|
fs.unlinkSync(dest);
|
|
28
28
|
}
|
|
@@ -39,18 +39,12 @@ function symlinkManagedSkill(src, dest, managedRoot) {
|
|
|
39
39
|
}
|
|
40
40
|
function removeManagedSkillLink(dest, managedRoot) {
|
|
41
41
|
try {
|
|
42
|
-
|
|
43
|
-
if (!stat.isSymbolicLink())
|
|
44
|
-
return;
|
|
45
|
-
const currentTarget = fs.readlinkSync(dest);
|
|
46
|
-
const resolvedTarget = path.resolve(path.dirname(dest), currentTarget);
|
|
47
|
-
const managedPrefix = path.resolve(managedRoot) + path.sep;
|
|
48
|
-
if (!resolvedTarget.startsWith(managedPrefix))
|
|
42
|
+
if (!isManagedSymlink(dest, managedRoot))
|
|
49
43
|
return;
|
|
50
44
|
fs.unlinkSync(dest);
|
|
51
45
|
}
|
|
52
46
|
catch (err) {
|
|
53
|
-
if (err.code !== "ENOENT" && (process.env.PHREN_DEBUG
|
|
47
|
+
if (err.code !== "ENOENT" && (process.env.PHREN_DEBUG)) {
|
|
54
48
|
process.stderr.write(`[phren] removeManagedSkillLink: ${errorMessage(err)}\n`);
|
|
55
49
|
}
|
|
56
50
|
}
|
|
@@ -64,6 +64,9 @@ function getProjectLocalSkills(phrenPath, project) {
|
|
|
64
64
|
return collectSkills(phrenPath, path.join(projectDir, "skills"), project, "project", "canonical", seen);
|
|
65
65
|
}
|
|
66
66
|
function skillPriority(skill) {
|
|
67
|
+
// "user" scope (priority 500) reserved for skills the user marks as untouchable —
|
|
68
|
+
// not yet wired to a scopeType value, but the priority slot is established here
|
|
69
|
+
// so the ordering is stable when we add it.
|
|
67
70
|
if (skill.scopeType === "project" && skill.sourceKind === "canonical")
|
|
68
71
|
return 400;
|
|
69
72
|
if (skill.scopeType === "global" && skill.sourceKind === "canonical")
|
package/mcp/dist/status.js
CHANGED
|
@@ -3,9 +3,10 @@ import * as path from "path";
|
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import { findPhrenPath, getProjectDirs, EXEC_TIMEOUT_QUICK_MS, debugLog, isRecord, hookConfigPath, homeDir, readRootManifest, } from "./shared.js";
|
|
5
5
|
import { buildIndex, detectProject, findFtsCacheForPath, listIndexedDocumentPaths, queryRows } from "./shared-index.js";
|
|
6
|
+
import { mergeConfig, getWorkflowPolicy } from "./shared-governance.js";
|
|
6
7
|
import { getMcpEnabledPreference, getHooksEnabledPreference } from "./init.js";
|
|
7
8
|
import { getTelemetrySummary } from "./telemetry.js";
|
|
8
|
-
import { runGit as runGitShared } from "./utils.js";
|
|
9
|
+
import { runGit as runGitShared, errorMessage } from "./utils.js";
|
|
9
10
|
import { readRuntimeHealth, resolveTaskFilePath } from "./data-access.js";
|
|
10
11
|
import { resolveRuntimeProfile } from "./runtime-profile.js";
|
|
11
12
|
import { renderPhrenArt } from "./phren-art.js";
|
|
@@ -17,8 +18,8 @@ function readPackageVersion() {
|
|
|
17
18
|
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
18
19
|
}
|
|
19
20
|
catch (err) {
|
|
20
|
-
if ((process.env.PHREN_DEBUG
|
|
21
|
-
process.stderr.write(`[phren] readPackageVersion: ${
|
|
21
|
+
if ((process.env.PHREN_DEBUG))
|
|
22
|
+
process.stderr.write(`[phren] readPackageVersion: ${errorMessage(err)}\n`);
|
|
22
23
|
return "unknown";
|
|
23
24
|
}
|
|
24
25
|
}
|
|
@@ -71,6 +72,33 @@ export async function runStatus() {
|
|
|
71
72
|
// Active project
|
|
72
73
|
if (activeProject) {
|
|
73
74
|
console.log(` ${DIM}project${RESET} ${activeProject}`);
|
|
75
|
+
// Effective config for this project
|
|
76
|
+
try {
|
|
77
|
+
const resolved = mergeConfig(phrenPath, activeProject);
|
|
78
|
+
const globalWorkflow = getWorkflowPolicy(phrenPath);
|
|
79
|
+
const projectSensitivity = resolved.findingSensitivity !== globalWorkflow.findingSensitivity
|
|
80
|
+
? `${resolved.findingSensitivity} ${DIM}(project override)${RESET}`
|
|
81
|
+
: resolved.findingSensitivity;
|
|
82
|
+
const projectTaskMode = resolved.taskMode !== globalWorkflow.taskMode
|
|
83
|
+
? `${resolved.taskMode} ${DIM}(project override)${RESET}`
|
|
84
|
+
: resolved.taskMode;
|
|
85
|
+
console.log(` ${DIM}sensitivity${RESET} ${projectSensitivity}`);
|
|
86
|
+
console.log(` ${DIM}task mode${RESET} ${projectTaskMode}`);
|
|
87
|
+
if (resolved.proactivity.base || resolved.proactivity.findings || resolved.proactivity.tasks) {
|
|
88
|
+
const parts = [];
|
|
89
|
+
if (resolved.proactivity.base)
|
|
90
|
+
parts.push(`base:${resolved.proactivity.base}`);
|
|
91
|
+
if (resolved.proactivity.findings)
|
|
92
|
+
parts.push(`findings:${resolved.proactivity.findings}`);
|
|
93
|
+
if (resolved.proactivity.tasks)
|
|
94
|
+
parts.push(`tasks:${resolved.proactivity.tasks}`);
|
|
95
|
+
console.log(` ${DIM}proactivity${RESET} ${parts.join(" ")} ${DIM}(project override)${RESET}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
if ((process.env.PHREN_DEBUG))
|
|
100
|
+
process.stderr.write(`[phren] statusConfig: ${errorMessage(err)}\n`);
|
|
101
|
+
}
|
|
74
102
|
}
|
|
75
103
|
// Phren path and config
|
|
76
104
|
console.log(` ${DIM}path${RESET} ${phrenPath}`);
|
|
@@ -102,8 +130,8 @@ export async function runStatus() {
|
|
|
102
130
|
mcpConfigured = Boolean(servers?.phren || servers?.phren);
|
|
103
131
|
}
|
|
104
132
|
catch (err) {
|
|
105
|
-
if ((process.env.PHREN_DEBUG
|
|
106
|
-
process.stderr.write(`[phren] statusWorkspaceMcp parse: ${
|
|
133
|
+
if ((process.env.PHREN_DEBUG))
|
|
134
|
+
process.stderr.write(`[phren] statusWorkspaceMcp parse: ${errorMessage(err)}\n`);
|
|
107
135
|
}
|
|
108
136
|
}
|
|
109
137
|
}
|
|
@@ -120,8 +148,8 @@ export async function runStatus() {
|
|
|
120
148
|
hooksInstalled = hookEvents.every((event) => hasCommandHook(hooks?.[event]));
|
|
121
149
|
}
|
|
122
150
|
catch (err) {
|
|
123
|
-
if ((process.env.PHREN_DEBUG
|
|
124
|
-
process.stderr.write(`[phren] statusHooks settingsParse: ${
|
|
151
|
+
if ((process.env.PHREN_DEBUG))
|
|
152
|
+
process.stderr.write(`[phren] statusHooks settingsParse: ${errorMessage(err)}\n`);
|
|
125
153
|
}
|
|
126
154
|
}
|
|
127
155
|
}
|
|
@@ -151,8 +179,8 @@ export async function runStatus() {
|
|
|
151
179
|
}
|
|
152
180
|
}
|
|
153
181
|
catch (err) {
|
|
154
|
-
if ((process.env.PHREN_DEBUG
|
|
155
|
-
process.stderr.write(`[phren] statusFtsIndex: ${
|
|
182
|
+
if ((process.env.PHREN_DEBUG))
|
|
183
|
+
process.stderr.write(`[phren] statusFtsIndex: ${errorMessage(err)}\n`);
|
|
156
184
|
}
|
|
157
185
|
const ftsLabel = ftsIndexOk
|
|
158
186
|
? `${GREEN}ok${RESET} ${DIM}(${ftsIndexSize > 0 ? `${(ftsIndexSize / 1024).toFixed(0)} KB` : `${ftsDocCount ?? 0} docs`})${RESET}`
|
|
@@ -186,8 +214,8 @@ export async function runStatus() {
|
|
|
186
214
|
}
|
|
187
215
|
}
|
|
188
216
|
catch (err) {
|
|
189
|
-
if ((process.env.PHREN_DEBUG
|
|
190
|
-
process.stderr.write(`[phren] statusSemantic: ${
|
|
217
|
+
if ((process.env.PHREN_DEBUG))
|
|
218
|
+
process.stderr.write(`[phren] statusSemantic: ${errorMessage(err)}\n`);
|
|
191
219
|
}
|
|
192
220
|
// Agent integration status
|
|
193
221
|
function hasPhrenEntry(filePath) {
|
|
@@ -198,8 +226,8 @@ export async function runStatus() {
|
|
|
198
226
|
return raw.includes('"phren"') || raw.includes("'phren'") || raw.includes('"phren"') || raw.includes("'phren'");
|
|
199
227
|
}
|
|
200
228
|
catch (err) {
|
|
201
|
-
if ((process.env.PHREN_DEBUG
|
|
202
|
-
process.stderr.write(`[phren] hasPhrenEntry: ${
|
|
229
|
+
if ((process.env.PHREN_DEBUG))
|
|
230
|
+
process.stderr.write(`[phren] hasPhrenEntry: ${errorMessage(err)}\n`);
|
|
203
231
|
return false;
|
|
204
232
|
}
|
|
205
233
|
}
|
package/mcp/dist/task-hygiene.js
CHANGED
|
@@ -141,7 +141,7 @@ function collectCorpus(root) {
|
|
|
141
141
|
texts.push(fs.readFileSync(fullPath, "utf8").slice(0, MAX_TEXT_BYTES).toLowerCase());
|
|
142
142
|
}
|
|
143
143
|
catch (err) {
|
|
144
|
-
if ((process.env.PHREN_DEBUG
|
|
144
|
+
if ((process.env.PHREN_DEBUG))
|
|
145
145
|
process.stderr.write(`[phren] task hygiene read ${fullPath}: ${errorMessage(err)}\n`);
|
|
146
146
|
}
|
|
147
147
|
if (filesSeen >= MAX_FILES_PER_ROOT)
|
package/mcp/dist/telemetry.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { runtimeDir } from "./shared.js";
|
|
4
|
+
import { errorMessage } from "./utils.js";
|
|
4
5
|
// In-memory buffers keyed by phrenPath to batch disk writes
|
|
5
6
|
// Keeping per-path buffers avoids silently losing events when the active path changes.
|
|
6
7
|
const buffers = new Map();
|
|
@@ -25,8 +26,8 @@ function loadFromDisk(phrenPath) {
|
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
catch (err) {
|
|
28
|
-
if ((process.env.PHREN_DEBUG
|
|
29
|
-
process.stderr.write(`[phren] telemetry loadFromDisk: ${
|
|
29
|
+
if ((process.env.PHREN_DEBUG))
|
|
30
|
+
process.stderr.write(`[phren] telemetry loadFromDisk: ${errorMessage(err)}\n`);
|
|
30
31
|
return defaults;
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -57,8 +58,8 @@ function flushTelemetryForPath(phrenPath) {
|
|
|
57
58
|
fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
|
|
58
59
|
}
|
|
59
60
|
catch (err) {
|
|
60
|
-
if ((process.env.PHREN_DEBUG
|
|
61
|
-
process.stderr.write(`[phren] telemetry flush: ${
|
|
61
|
+
if ((process.env.PHREN_DEBUG))
|
|
62
|
+
process.stderr.write(`[phren] telemetry flush: ${errorMessage(err)}\n`);
|
|
62
63
|
}
|
|
63
64
|
pendingCounts.set(phrenPath, 0);
|
|
64
65
|
}
|
package/mcp/dist/update.js
CHANGED
|
@@ -63,7 +63,7 @@ export async function runPhrenUpdate(opts = {}) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
catch (err) {
|
|
66
|
-
if ((process.env.PHREN_DEBUG
|
|
66
|
+
if ((process.env.PHREN_DEBUG))
|
|
67
67
|
process.stderr.write(`[phren] runPhrenUpdate gitStatus: ${errorMessage(err)}\n`);
|
|
68
68
|
}
|
|
69
69
|
const pull = run("git", ["pull", "--rebase", "--autostash"], root);
|
package/mcp/dist/utils.js
CHANGED
|
@@ -12,7 +12,7 @@ function loadSynonymsJson(fileName) {
|
|
|
12
12
|
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
13
13
|
}
|
|
14
14
|
catch (err) {
|
|
15
|
-
if ((process.env.PHREN_DEBUG
|
|
15
|
+
if ((process.env.PHREN_DEBUG))
|
|
16
16
|
process.stderr.write(`[phren] ${fileName} load failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
17
17
|
return {};
|
|
18
18
|
}
|
|
@@ -276,7 +276,7 @@ function parseSynonymsYaml(filePath) {
|
|
|
276
276
|
return loaded;
|
|
277
277
|
}
|
|
278
278
|
catch (err) {
|
|
279
|
-
if ((process.env.PHREN_DEBUG
|
|
279
|
+
if ((process.env.PHREN_DEBUG))
|
|
280
280
|
process.stderr.write(`[phren] synonyms.yaml parse failed (${filePath}): ${err instanceof Error ? err.message : String(err)}\n`);
|
|
281
281
|
return {};
|
|
282
282
|
}
|
|
@@ -315,7 +315,7 @@ function parseLearnedSynonymsJson(filePath) {
|
|
|
315
315
|
return loaded;
|
|
316
316
|
}
|
|
317
317
|
catch (err) {
|
|
318
|
-
if ((process.env.PHREN_DEBUG
|
|
318
|
+
if ((process.env.PHREN_DEBUG))
|
|
319
319
|
process.stderr.write(`[phren] learned-synonyms parse failed (${filePath}): ${err instanceof Error ? err.message : String(err)}\n`);
|
|
320
320
|
return {};
|
|
321
321
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phren/cli",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Knowledge layer for AI agents.
|
|
3
|
+
"version": "0.0.11",
|
|
4
|
+
"description": "Knowledge layer for AI agents. Phren learns and recalls.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"phren": "mcp/dist/index.js"
|