@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
|
@@ -4,7 +4,7 @@ import { queryDocRows, queryRows, cosineFallback, extractSnippet, getDocSourceKe
|
|
|
4
4
|
import { filterTrustedFindingsDetailed, } from "./shared-content.js";
|
|
5
5
|
import { parseCitationComment } from "./content-citation.js";
|
|
6
6
|
import { getHighImpactFindings } from "./finding-impact.js";
|
|
7
|
-
import { buildFtsQueryVariants, buildRelaxedFtsQuery, isFeatureEnabled, STOP_WORDS } from "./utils.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,17 +34,23 @@ 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
|
}
|
|
@@ -385,8 +391,8 @@ export async function searchDocumentsAsync(db, safeQuery, prompt, keywords, dete
|
|
|
385
391
|
}
|
|
386
392
|
catch (err) {
|
|
387
393
|
// Vector search failure is non-fatal — return sync result
|
|
388
|
-
if ((process.env.PHREN_DEBUG
|
|
389
|
-
process.stderr.write(`[phren] hybridSearch vectorFallback: ${
|
|
394
|
+
if ((process.env.PHREN_DEBUG))
|
|
395
|
+
process.stderr.write(`[phren] hybridSearch vectorFallback: ${errorMessage(err)}\n`);
|
|
390
396
|
return syncResult;
|
|
391
397
|
}
|
|
392
398
|
}
|
|
@@ -445,7 +451,7 @@ export async function searchKnowledgeRows(db, options) {
|
|
|
445
451
|
}
|
|
446
452
|
}
|
|
447
453
|
catch (err) {
|
|
448
|
-
debugLog(`rowid dedup query failed: ${
|
|
454
|
+
debugLog(`rowid dedup query failed: ${errorMessage(err)}`);
|
|
449
455
|
}
|
|
450
456
|
const cosineResults = cosineFallback(db, query, ftsRowids, maxResults - rows.length)
|
|
451
457
|
.filter((doc) => (!filterProject || doc.project === filterProject) && (!filterType || doc.type === filterType));
|
|
@@ -485,8 +491,8 @@ export async function searchKnowledgeRows(db, options) {
|
|
|
485
491
|
}
|
|
486
492
|
}
|
|
487
493
|
catch (err) {
|
|
488
|
-
if ((process.env.PHREN_DEBUG
|
|
489
|
-
process.stderr.write(`[phren] vectorFallback: ${
|
|
494
|
+
if ((process.env.PHREN_DEBUG)) {
|
|
495
|
+
process.stderr.write(`[phren] vectorFallback: ${errorMessage(err)}\n`);
|
|
490
496
|
}
|
|
491
497
|
}
|
|
492
498
|
}
|
|
@@ -732,8 +738,8 @@ export function markStaleCitations(snippet) {
|
|
|
732
738
|
}
|
|
733
739
|
}
|
|
734
740
|
catch (err) {
|
|
735
|
-
if ((process.env.PHREN_DEBUG
|
|
736
|
-
process.stderr.write(`[phren] applyCitationAnnotations fileRead: ${
|
|
741
|
+
if ((process.env.PHREN_DEBUG))
|
|
742
|
+
process.stderr.write(`[phren] applyCitationAnnotations fileRead: ${errorMessage(err)}\n`);
|
|
737
743
|
stale = true;
|
|
738
744
|
}
|
|
739
745
|
}
|
|
@@ -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"
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audit
|
|
3
|
+
description: Full codebase audit — dead code, security, dependencies, performance, optimization. Not a diff review — scans everything.
|
|
4
|
+
---
|
|
5
|
+
# /audit - Full Codebase Audit
|
|
6
|
+
|
|
7
|
+
Unlike `/simplify` (which reviews your last diff), this scans the entire codebase. Run it when you want to clean house.
|
|
8
|
+
|
|
9
|
+
## What It Does
|
|
10
|
+
|
|
11
|
+
Launch 5 parallel agents. Each one scans the full codebase for a different class of problem. When they're done, aggregate findings and fix what's fixable.
|
|
12
|
+
|
|
13
|
+
## Phase 1: Discover the Codebase
|
|
14
|
+
|
|
15
|
+
Before launching agents, understand the project:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
1. Read package.json (or pyproject.toml, Cargo.toml, go.mod — whatever applies)
|
|
19
|
+
2. Find the source directories (src/, lib/, app/, etc.)
|
|
20
|
+
3. Count files by extension to understand the stack
|
|
21
|
+
4. Check for existing lint/test configs
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Pass this context to every agent so they know where to look.
|
|
25
|
+
|
|
26
|
+
## Phase 2: Launch 5 Agents in Parallel
|
|
27
|
+
|
|
28
|
+
Use the Agent tool to launch all five concurrently in a single message. Give each agent the project context from Phase 1.
|
|
29
|
+
|
|
30
|
+
### Agent 1: Dead Code & Unused Exports
|
|
31
|
+
|
|
32
|
+
Find code that exists but isn't used:
|
|
33
|
+
|
|
34
|
+
1. **Unused exports.** For every `export` in the codebase, check if it's imported anywhere. Flag exports that are only used in their own file or not used at all. Exclude entry points and public API surfaces.
|
|
35
|
+
2. **Unused dependencies.** Cross-reference `package.json` dependencies against actual imports in source files. Flag packages that are installed but never imported. Check devDependencies too — are test utilities actually used in tests?
|
|
36
|
+
3. **Dead functions.** Functions defined but never called. Methods on classes that nothing invokes. Event handlers registered but for events that are never emitted.
|
|
37
|
+
4. **Orphan files.** Files that nothing imports. Test files for source files that no longer exist. Config files for tools that aren't in the project.
|
|
38
|
+
5. **Feature flags that resolved.** Environment variable checks where one branch is clearly dead. TODO/FIXME/HACK comments older than 6 months.
|
|
39
|
+
6. **Stale type definitions.** Interfaces or types that nothing references. Generic type parameters that are always the same concrete type.
|
|
40
|
+
|
|
41
|
+
### Agent 2: Security Scan
|
|
42
|
+
|
|
43
|
+
Look for vulnerabilities in the actual code (not just `npm audit`):
|
|
44
|
+
|
|
45
|
+
1. **Injection vectors.** Shell commands built from user input without sanitization. SQL queries with string concatenation. HTML built from unescaped variables. Regex built from user input (ReDoS).
|
|
46
|
+
2. **Path traversal.** Any file operation where the path comes from user input or external data without validation. Check for `../` normalization and symlink following.
|
|
47
|
+
3. **Secret exposure.** Hardcoded API keys, tokens, passwords. Environment variables logged or included in error messages. Secrets in URLs or query parameters.
|
|
48
|
+
4. **Insecure defaults.** TLS verification disabled. CORS set to `*`. Cookies without secure/httponly/samesite flags. Debug modes that shouldn't ship.
|
|
49
|
+
5. **Dependency vulnerabilities.** Run `npm audit` (or equivalent). Check for known CVEs. Flag dependencies that haven't been updated in 12+ months.
|
|
50
|
+
6. **Auth and access control.** Endpoints or functions that should check permissions but don't. Token validation that's incomplete. Rate limiting gaps.
|
|
51
|
+
7. **SSRF and network.** Outbound requests where the URL comes from user input. Webhook/callback URLs that aren't validated against internal networks.
|
|
52
|
+
|
|
53
|
+
### Agent 3: Performance & Efficiency
|
|
54
|
+
|
|
55
|
+
Find code that wastes time or resources:
|
|
56
|
+
|
|
57
|
+
1. **Startup cost.** What runs at import/require time? Top-level await, synchronous file reads, heavy initialization that could be lazy.
|
|
58
|
+
2. **N+1 patterns.** Loops that make a network/disk/DB call per iteration instead of batching.
|
|
59
|
+
3. **Redundant computation.** The same value computed multiple times in a hot path. Missing memoization where inputs rarely change. Expensive operations inside loops that could be hoisted.
|
|
60
|
+
4. **Blocking operations.** Synchronous file I/O on async paths. `JSON.parse` on large payloads without streaming. CPU-heavy work on the event loop.
|
|
61
|
+
5. **Unbounded growth.** Caches without eviction. Arrays that grow without limits. Event listeners that are added but never removed. Intervals that are set but never cleared.
|
|
62
|
+
6. **Over-fetching.** Loading entire files when you need one field. Importing a whole library for one function. Reading all records when filtering for a subset.
|
|
63
|
+
7. **Missed parallelism.** Sequential `await` calls that are independent and could use `Promise.all`. File operations that could be batched.
|
|
64
|
+
|
|
65
|
+
### Agent 4: Code Quality & Simplification
|
|
66
|
+
|
|
67
|
+
Find code that's more complicated than it needs to be:
|
|
68
|
+
|
|
69
|
+
1. **Abstraction debt.** Functions over 80 lines. Files over 500 lines. Classes that do too many things. Deeply nested conditionals (3+ levels).
|
|
70
|
+
2. **Copy-paste code.** Near-duplicate blocks across files. Similar switch/case statements that should share logic. Functions that differ by one parameter.
|
|
71
|
+
3. **Unnecessary indirection.** Wrapper functions that add nothing. Abstract classes with one implementation. Factory patterns for objects that are only created once.
|
|
72
|
+
4. **Type complexity.** Union types with 5+ members. Generic types nested 3+ levels deep. Type assertions (`as`) that could be avoided with better typing.
|
|
73
|
+
5. **Error handling.** Empty catch blocks. Catch-and-rethrow without adding context. Inconsistent error types across similar operations.
|
|
74
|
+
6. **Naming.** Boolean variables that don't read as questions. Functions whose names don't describe what they return. Abbreviations that only the author understands.
|
|
75
|
+
7. **Stale patterns.** Callbacks where promises/async would be cleaner. Manual iteration where array methods would work. Hand-rolled utilities where the language or a dependency provides it.
|
|
76
|
+
|
|
77
|
+
### Agent 5: Dependency Health
|
|
78
|
+
|
|
79
|
+
Audit the dependency tree:
|
|
80
|
+
|
|
81
|
+
1. **Outdated packages.** Run `npm outdated` (or equivalent). Flag major version bumps that are available. Note any breaking changes.
|
|
82
|
+
2. **Heavy dependencies.** Check bundle/install size. Flag packages over 1MB that could be replaced with lighter alternatives or native APIs.
|
|
83
|
+
3. **Duplicate functionality.** Multiple packages that do the same thing (e.g., both `lodash` and `underscore`, or `axios` and `node-fetch`).
|
|
84
|
+
4. **License issues.** Check for GPL or other copyleft licenses in a non-GPL project. Flag any "unknown" licenses.
|
|
85
|
+
5. **Abandoned packages.** Dependencies with no commits in 2+ years, or archived repos.
|
|
86
|
+
6. **Phantom dependencies.** Imports that resolve only because a parent dependency installs them (not in your own package.json).
|
|
87
|
+
|
|
88
|
+
## Phase 3: Triage and Fix
|
|
89
|
+
|
|
90
|
+
Wait for all agents. Then:
|
|
91
|
+
|
|
92
|
+
1. **Deduplicate.** Multiple agents may flag the same issue from different angles. Merge them.
|
|
93
|
+
2. **Prioritize.** Security issues first. Then dead code (easy wins). Then performance. Then quality.
|
|
94
|
+
3. **Fix directly.** Don't just report — fix what you can. For things that need the user's input (like removing a dependency that might be used in a way you can't see), ask.
|
|
95
|
+
4. **Summarize.** Report what was found, what was fixed, and what needs the user's decision.
|
|
96
|
+
|
|
97
|
+
## Options
|
|
98
|
+
|
|
99
|
+
The user can scope the audit:
|
|
100
|
+
|
|
101
|
+
- `/audit` — full audit, all 5 agents
|
|
102
|
+
- `/audit security` — just the security agent
|
|
103
|
+
- `/audit dead-code` — just dead code detection
|
|
104
|
+
- `/audit performance` — just performance
|
|
105
|
+
- `/audit deps` — just dependency health
|
|
106
|
+
- `/audit quality` — just code quality
|
package/mcp/dist/shared-paths.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./phren-paths.js";
|