@phren/cli 0.0.10 → 0.0.12
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 +11 -17
- package/mcp/dist/capabilities/cli.js +1 -1
- package/mcp/dist/capabilities/mcp.js +1 -1
- package/mcp/dist/capabilities/vscode.js +1 -1
- package/mcp/dist/capabilities/web-ui.js +1 -1
- package/mcp/dist/cli-actions.js +58 -71
- package/mcp/dist/cli-config.js +337 -131
- package/mcp/dist/cli-extract.js +3 -2
- package/mcp/dist/cli-govern.js +35 -63
- package/mcp/dist/cli-graph.js +19 -4
- package/mcp/dist/cli-hooks-globs.js +2 -1
- package/mcp/dist/cli-hooks-output.js +4 -4
- package/mcp/dist/cli-hooks-session.js +1 -1
- package/mcp/dist/cli-hooks.js +44 -35
- package/mcp/dist/cli-namespaces.js +15 -5
- package/mcp/dist/cli-search.js +2 -2
- package/mcp/dist/cli.js +1 -1
- package/mcp/dist/content-archive.js +23 -14
- package/mcp/dist/content-citation.js +13 -2
- package/mcp/dist/content-dedup.js +9 -9
- package/mcp/dist/content-learning.js +6 -4
- package/mcp/dist/content-metadata.js +10 -0
- package/mcp/dist/core-finding.js +1 -1
- package/mcp/dist/data-access.js +10 -31
- package/mcp/dist/data-tasks.js +5 -26
- package/mcp/dist/embedding.js +7 -8
- package/mcp/dist/entrypoint.js +133 -102
- package/mcp/dist/finding-impact.js +1 -32
- package/mcp/dist/finding-journal.js +1 -1
- package/mcp/dist/finding-lifecycle.js +2 -7
- package/mcp/dist/governance-locks.js +12 -5
- package/mcp/dist/governance-policy.js +156 -9
- package/mcp/dist/governance-scores.js +4 -10
- package/mcp/dist/hooks.js +62 -18
- package/mcp/dist/index.js +4 -4
- package/mcp/dist/init-config.js +4 -25
- package/mcp/dist/init-preferences.js +1 -1
- package/mcp/dist/init-setup.js +6 -55
- package/mcp/dist/init-shared.js +53 -1
- package/mcp/dist/init.js +191 -29
- package/mcp/dist/link-checksums.js +3 -2
- package/mcp/dist/link-context.js +2 -2
- package/mcp/dist/link-doctor.js +14 -57
- package/mcp/dist/link-skills.js +98 -12
- package/mcp/dist/link.js +16 -75
- 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 +12 -7
- package/mcp/dist/mcp-extract.js +2 -2
- package/mcp/dist/mcp-finding.js +16 -20
- package/mcp/dist/mcp-graph.js +12 -12
- package/mcp/dist/mcp-hooks.js +1 -1
- package/mcp/dist/mcp-ops.js +18 -18
- package/mcp/dist/mcp-search.js +11 -16
- package/mcp/dist/mcp-session.js +12 -2
- package/mcp/dist/memory-ui-assets.js +1 -36
- package/mcp/dist/memory-ui-graph.js +152 -50
- package/mcp/dist/memory-ui-page.js +30 -5
- package/mcp/dist/memory-ui-scripts.js +252 -63
- package/mcp/dist/memory-ui-server.js +115 -3
- package/mcp/dist/phren-core.js +2 -0
- package/mcp/dist/phren-paths.js +8 -9
- package/mcp/dist/proactivity.js +5 -5
- package/mcp/dist/profile-store.js +2 -2
- package/mcp/dist/project-config.js +64 -17
- package/mcp/dist/provider-adapters.js +1 -1
- package/mcp/dist/query-correlation.js +22 -19
- package/mcp/dist/session-checkpoints.js +14 -14
- package/mcp/dist/session-utils.js +3 -2
- package/mcp/dist/shared-data-utils.js +28 -0
- package/mcp/dist/shared-fragment-graph.js +22 -21
- package/mcp/dist/shared-governance.js +1 -1
- package/mcp/dist/shared-index.js +144 -105
- package/mcp/dist/shared-retrieval.js +21 -23
- package/mcp/dist/shared-search-fallback.js +15 -25
- package/mcp/dist/shared-sqljs.js +3 -2
- package/mcp/dist/shared.js +5 -6
- package/mcp/dist/shell-entry.js +1 -1
- package/mcp/dist/shell-input.js +63 -53
- package/mcp/dist/shell-palette.js +6 -1
- package/mcp/dist/shell-render.js +9 -5
- package/mcp/dist/shell-state-store.js +2 -5
- package/mcp/dist/shell-view.js +7 -6
- package/mcp/dist/shell.js +5 -55
- package/mcp/dist/skill-files.js +4 -10
- package/mcp/dist/skill-registry.js +3 -0
- package/mcp/dist/status.js +43 -21
- 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 +4 -4
- package/package.json +2 -3
- package/skills/docs.md +11 -11
- package/starter/README.md +1 -1
- package/starter/global/CLAUDE.md +2 -2
- package/starter/global/skills/audit.md +106 -0
- package/mcp/dist/cli-hooks-retrieval.js +0 -2
- package/mcp/dist/impact-scoring.js +0 -22
- package/mcp/dist/shared-paths.js +0 -1
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,12 +3,14 @@ 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";
|
|
13
|
+
import { RESET, BOLD, DIM, GREEN, YELLOW, RED, CYAN } from "./shell-render.js";
|
|
12
14
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
15
|
function readPackageVersion() {
|
|
14
16
|
try {
|
|
@@ -17,18 +19,11 @@ function readPackageVersion() {
|
|
|
17
19
|
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
18
20
|
}
|
|
19
21
|
catch (err) {
|
|
20
|
-
if ((process.env.PHREN_DEBUG
|
|
21
|
-
process.stderr.write(`[phren] readPackageVersion: ${
|
|
22
|
+
if ((process.env.PHREN_DEBUG))
|
|
23
|
+
process.stderr.write(`[phren] readPackageVersion: ${errorMessage(err)}\n`);
|
|
22
24
|
return "unknown";
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
|
-
const RESET = "\x1b[0m";
|
|
26
|
-
const BOLD = "\x1b[1m";
|
|
27
|
-
const DIM = "\x1b[2m";
|
|
28
|
-
const GREEN = "\x1b[32m";
|
|
29
|
-
const YELLOW = "\x1b[33m";
|
|
30
|
-
const RED = "\x1b[31m";
|
|
31
|
-
const CYAN = "\x1b[36m";
|
|
32
27
|
function check(ok) {
|
|
33
28
|
return ok ? `${GREEN}ok${RESET}` : `${RED}missing${RESET}`;
|
|
34
29
|
}
|
|
@@ -71,6 +66,33 @@ export async function runStatus() {
|
|
|
71
66
|
// Active project
|
|
72
67
|
if (activeProject) {
|
|
73
68
|
console.log(` ${DIM}project${RESET} ${activeProject}`);
|
|
69
|
+
// Effective config for this project
|
|
70
|
+
try {
|
|
71
|
+
const resolved = mergeConfig(phrenPath, activeProject);
|
|
72
|
+
const globalWorkflow = getWorkflowPolicy(phrenPath);
|
|
73
|
+
const projectSensitivity = resolved.findingSensitivity !== globalWorkflow.findingSensitivity
|
|
74
|
+
? `${resolved.findingSensitivity} ${DIM}(project override)${RESET}`
|
|
75
|
+
: resolved.findingSensitivity;
|
|
76
|
+
const projectTaskMode = resolved.taskMode !== globalWorkflow.taskMode
|
|
77
|
+
? `${resolved.taskMode} ${DIM}(project override)${RESET}`
|
|
78
|
+
: resolved.taskMode;
|
|
79
|
+
console.log(` ${DIM}sensitivity${RESET} ${projectSensitivity}`);
|
|
80
|
+
console.log(` ${DIM}task mode${RESET} ${projectTaskMode}`);
|
|
81
|
+
if (resolved.proactivity.base || resolved.proactivity.findings || resolved.proactivity.tasks) {
|
|
82
|
+
const parts = [];
|
|
83
|
+
if (resolved.proactivity.base)
|
|
84
|
+
parts.push(`base:${resolved.proactivity.base}`);
|
|
85
|
+
if (resolved.proactivity.findings)
|
|
86
|
+
parts.push(`findings:${resolved.proactivity.findings}`);
|
|
87
|
+
if (resolved.proactivity.tasks)
|
|
88
|
+
parts.push(`tasks:${resolved.proactivity.tasks}`);
|
|
89
|
+
console.log(` ${DIM}proactivity${RESET} ${parts.join(" ")} ${DIM}(project override)${RESET}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if ((process.env.PHREN_DEBUG))
|
|
94
|
+
process.stderr.write(`[phren] statusConfig: ${errorMessage(err)}\n`);
|
|
95
|
+
}
|
|
74
96
|
}
|
|
75
97
|
// Phren path and config
|
|
76
98
|
console.log(` ${DIM}path${RESET} ${phrenPath}`);
|
|
@@ -102,8 +124,8 @@ export async function runStatus() {
|
|
|
102
124
|
mcpConfigured = Boolean(servers?.phren || servers?.phren);
|
|
103
125
|
}
|
|
104
126
|
catch (err) {
|
|
105
|
-
if ((process.env.PHREN_DEBUG
|
|
106
|
-
process.stderr.write(`[phren] statusWorkspaceMcp parse: ${
|
|
127
|
+
if ((process.env.PHREN_DEBUG))
|
|
128
|
+
process.stderr.write(`[phren] statusWorkspaceMcp parse: ${errorMessage(err)}\n`);
|
|
107
129
|
}
|
|
108
130
|
}
|
|
109
131
|
}
|
|
@@ -120,8 +142,8 @@ export async function runStatus() {
|
|
|
120
142
|
hooksInstalled = hookEvents.every((event) => hasCommandHook(hooks?.[event]));
|
|
121
143
|
}
|
|
122
144
|
catch (err) {
|
|
123
|
-
if ((process.env.PHREN_DEBUG
|
|
124
|
-
process.stderr.write(`[phren] statusHooks settingsParse: ${
|
|
145
|
+
if ((process.env.PHREN_DEBUG))
|
|
146
|
+
process.stderr.write(`[phren] statusHooks settingsParse: ${errorMessage(err)}\n`);
|
|
125
147
|
}
|
|
126
148
|
}
|
|
127
149
|
}
|
|
@@ -151,8 +173,8 @@ export async function runStatus() {
|
|
|
151
173
|
}
|
|
152
174
|
}
|
|
153
175
|
catch (err) {
|
|
154
|
-
if ((process.env.PHREN_DEBUG
|
|
155
|
-
process.stderr.write(`[phren] statusFtsIndex: ${
|
|
176
|
+
if ((process.env.PHREN_DEBUG))
|
|
177
|
+
process.stderr.write(`[phren] statusFtsIndex: ${errorMessage(err)}\n`);
|
|
156
178
|
}
|
|
157
179
|
const ftsLabel = ftsIndexOk
|
|
158
180
|
? `${GREEN}ok${RESET} ${DIM}(${ftsIndexSize > 0 ? `${(ftsIndexSize / 1024).toFixed(0)} KB` : `${ftsDocCount ?? 0} docs`})${RESET}`
|
|
@@ -186,8 +208,8 @@ export async function runStatus() {
|
|
|
186
208
|
}
|
|
187
209
|
}
|
|
188
210
|
catch (err) {
|
|
189
|
-
if ((process.env.PHREN_DEBUG
|
|
190
|
-
process.stderr.write(`[phren] statusSemantic: ${
|
|
211
|
+
if ((process.env.PHREN_DEBUG))
|
|
212
|
+
process.stderr.write(`[phren] statusSemantic: ${errorMessage(err)}\n`);
|
|
191
213
|
}
|
|
192
214
|
// Agent integration status
|
|
193
215
|
function hasPhrenEntry(filePath) {
|
|
@@ -198,8 +220,8 @@ export async function runStatus() {
|
|
|
198
220
|
return raw.includes('"phren"') || raw.includes("'phren'") || raw.includes('"phren"') || raw.includes("'phren'");
|
|
199
221
|
}
|
|
200
222
|
catch (err) {
|
|
201
|
-
if ((process.env.PHREN_DEBUG
|
|
202
|
-
process.stderr.write(`[phren] hasPhrenEntry: ${
|
|
223
|
+
if ((process.env.PHREN_DEBUG))
|
|
224
|
+
process.stderr.write(`[phren] hasPhrenEntry: ${errorMessage(err)}\n`);
|
|
203
225
|
return false;
|
|
204
226
|
}
|
|
205
227
|
}
|
|
@@ -274,7 +296,7 @@ export async function runStatus() {
|
|
|
274
296
|
totalTask += countBullets(taskPath);
|
|
275
297
|
totalQueue += countQueueItems(phrenPath, projName);
|
|
276
298
|
}
|
|
277
|
-
console.log(`\n ${DIM}phren holds${RESET} ${projectDirs.length} projects, ${totalFindings}
|
|
299
|
+
console.log(`\n ${DIM}phren holds${RESET} ${projectDirs.length} projects, ${totalFindings} findings, ${totalTask} tasks, ${totalQueue} queued`);
|
|
278
300
|
const gitTarget = manifest?.installMode === "project-local" && manifest.workspaceRoot ? manifest.workspaceRoot : phrenPath;
|
|
279
301
|
const isGitRepo = runGit(gitTarget, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
280
302
|
const hasOriginRemote = isGitRepo && Boolean(runGit(gitTarget, ["remote", "get-url", "origin"]));
|
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
|
}
|
|
@@ -241,7 +241,7 @@ export function sanitizeFts5Query(raw) {
|
|
|
241
241
|
if (raw.length > 500)
|
|
242
242
|
raw = raw.slice(0, 500);
|
|
243
243
|
// Whitelist approach: only allow alphanumeric, spaces, hyphens, apostrophes, double quotes, asterisks
|
|
244
|
-
let q = raw.replace(/[^a-zA-Z0-9 \-
|
|
244
|
+
let q = raw.replace(/[^a-zA-Z0-9 \-"*]/g, " ");
|
|
245
245
|
q = q.replace(/\s+/g, " ");
|
|
246
246
|
q = q.trim();
|
|
247
247
|
// Q83: FTS5 only accepts * as a prefix operator directly attached to a token
|
|
@@ -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.12",
|
|
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"
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"glob": "^13.0.6",
|
|
19
19
|
"inquirer": "^12.10.0",
|
|
20
20
|
"js-yaml": "^4.1.1",
|
|
21
|
-
"sharp": "^0.34.5",
|
|
22
21
|
"sql.js-fts5": "^1.4.0",
|
|
23
22
|
"zod": "^4.3.6"
|
|
24
23
|
},
|
package/skills/docs.md
CHANGED
|
@@ -74,10 +74,10 @@ These numbers appear in multiple files and must all agree:
|
|
|
74
74
|
|
|
75
75
|
| Item | Where to verify |
|
|
76
76
|
|------|----------------|
|
|
77
|
-
| MCP tool count | `mcp/src/index.ts
|
|
78
|
-
| CLI subcommand count | `mcp/src/cli.ts
|
|
77
|
+
| MCP tool count | `mcp/src/index.ts`: count `server.tool(` calls |
|
|
78
|
+
| CLI subcommand count | `mcp/src/cli.ts`: count `program.command(` calls |
|
|
79
79
|
| Version | `package.json` → `version` field |
|
|
80
|
-
| Hook events | `mcp/src/init.ts
|
|
80
|
+
| Hook events | `mcp/src/init.ts`: look for hook registration |
|
|
81
81
|
|
|
82
82
|
Run `/parity` if it is installed to automate numeric cross-checking across surfaces.
|
|
83
83
|
|
|
@@ -95,7 +95,7 @@ Sections to verify and update:
|
|
|
95
95
|
- **Tool count** in any summary sentence (e.g. "65 MCP tools")
|
|
96
96
|
|
|
97
97
|
When updating the HTML:
|
|
98
|
-
- Keep existing structure and CSS classes
|
|
98
|
+
- Keep existing structure and CSS classes. Do not restructure the page
|
|
99
99
|
- Update text content only; do not rewrite layout
|
|
100
100
|
- Preserve the `<details>` blocks for env var categories if they exist
|
|
101
101
|
|
|
@@ -103,12 +103,12 @@ When updating the HTML:
|
|
|
103
103
|
|
|
104
104
|
These files are consumed by LLMs directly. Keep them plain text with no HTML.
|
|
105
105
|
|
|
106
|
-
`llms.txt
|
|
106
|
+
`llms.txt`, short summary (under 60 lines):
|
|
107
107
|
- Tool count
|
|
108
108
|
- Install command
|
|
109
109
|
- One-line description of what phren does
|
|
110
110
|
|
|
111
|
-
`llms-full.txt
|
|
111
|
+
`llms-full.txt`, comprehensive reference:
|
|
112
112
|
- All MCP tool signatures with descriptions
|
|
113
113
|
- All CLI commands
|
|
114
114
|
- All environment variables
|
|
@@ -122,7 +122,7 @@ The CLI commands block must list every top-level command. Add or remove lines to
|
|
|
122
122
|
|
|
123
123
|
### 7. Sync version references
|
|
124
124
|
|
|
125
|
-
`CHANGELOG.md`: the top entry's version must match `package.json`. If a new version was bumped but the changelog has no entry yet, note it
|
|
125
|
+
`CHANGELOG.md`: the top entry's version must match `package.json`. If a new version was bumped but the changelog has no entry yet, note it. Do not fabricate one.
|
|
126
126
|
|
|
127
127
|
`README.md` and `docs/index.html`: update any hardcoded version strings (badges, `npm install @phren/cli@X.Y.Z`, etc.).
|
|
128
128
|
|
|
@@ -150,18 +150,18 @@ No changes needed:
|
|
|
150
150
|
- mcp/README.md: tool count correct
|
|
151
151
|
|
|
152
152
|
Warnings:
|
|
153
|
-
- docs/faq.md: references `phren init
|
|
153
|
+
- docs/faq.md: references `phren init`, command was renamed to `phren link` in v0.0.7
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
List every file checked. Be specific about what changed and what line/section.
|
|
157
157
|
|
|
158
158
|
## What not to do
|
|
159
159
|
|
|
160
|
-
- Do not restructure or reformat documentation for style
|
|
160
|
+
- Do not restructure or reformat documentation for style. Only fix accuracy
|
|
161
161
|
- Do not add new sections or features to the docs without user direction
|
|
162
162
|
- Do not fabricate changelog entries for unreleased versions
|
|
163
|
-
- Do not silently skip a surface because it looks "probably fine"
|
|
164
|
-
- Do not guess tool counts
|
|
163
|
+
- Do not silently skip a surface because it looks "probably fine". Check all of them
|
|
164
|
+
- Do not guess tool counts. Always derive from `grep` on the source file
|
|
165
165
|
|
|
166
166
|
## Related skills
|
|
167
167
|
|
package/starter/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# My Phren
|
|
2
2
|
|
|
3
|
-
Your personal project store for [phren](https://github.com/alaarab/phren). Phren is your project's memory keeper
|
|
3
|
+
Your personal project store for [phren](https://github.com/alaarab/phren). Phren is your project's memory keeper. He holds what your agents learn and surfaces it when it matters, across sessions and machines.
|
|
4
4
|
|
|
5
5
|
## Structure
|
|
6
6
|
|
package/starter/global/CLAUDE.md
CHANGED
|
@@ -56,7 +56,7 @@ Run `/phren-sync` to pull everything down or push changes back.
|
|
|
56
56
|
|
|
57
57
|
## MCP tools
|
|
58
58
|
|
|
59
|
-
The phren MCP server is running. Phren already knows a lot
|
|
59
|
+
The phren MCP server is running. Phren already knows a lot. Ask him before asking the user to repeat themselves.
|
|
60
60
|
|
|
61
61
|
- **At session start:** ask phren what's active: `list_projects()`, then `get_project_summary(name)` for the relevant project
|
|
62
62
|
- **When the user mentions a project, codebase, or task:** ask phren first: `search_knowledge(query)` before asking questions
|
|
@@ -86,4 +86,4 @@ Read `~/.phren-context.md` at the start of every session for machine-specific co
|
|
|
86
86
|
|
|
87
87
|
## Without MCP server
|
|
88
88
|
|
|
89
|
-
If the MCP server isn't available, phren still helps. Claude reads `~/.phren-context.md` and per-project memory files from `~/.claude/projects/` for context, then fetches details directly from `~/.phren/project-name/` as needed. No MCP required
|
|
89
|
+
If the MCP server isn't available, phren still helps. Claude reads `~/.phren-context.md` and per-project memory files from `~/.claude/projects/` for context, then fetches details directly from `~/.phren/project-name/` as needed. No MCP required, phren just works a bit more quietly.
|
|
@@ -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
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// Backward-compatible wrapper around finding-impact.
|
|
2
|
-
import { findingIdFromLine, extractFindingIdsFromSnippet, logImpact, getHighImpactFindings, markImpactEntriesCompletedForSession, } from "./finding-impact.js";
|
|
3
|
-
export function impactEntryKey(project, findingId) {
|
|
4
|
-
return `${project}\u0000${findingId}`;
|
|
5
|
-
}
|
|
6
|
-
export { findingIdFromLine, extractFindingIdsFromSnippet, markImpactEntriesCompletedForSession, };
|
|
7
|
-
export function appendImpactEntries(phrenPath, entries) {
|
|
8
|
-
const pending = entries.filter((entry) => !entry.taskCompleted);
|
|
9
|
-
if (pending.length === 0)
|
|
10
|
-
return;
|
|
11
|
-
logImpact(phrenPath, pending.map((entry) => ({
|
|
12
|
-
findingId: entry.findingId,
|
|
13
|
-
project: entry.project,
|
|
14
|
-
sessionId: entry.sessionId,
|
|
15
|
-
})));
|
|
16
|
-
}
|
|
17
|
-
export function getHighImpactFindingKeys(phrenPath, minSuccessCount = 3) {
|
|
18
|
-
const findingIds = getHighImpactFindings(phrenPath, minSuccessCount);
|
|
19
|
-
// Legacy API encoded project+findingId; new API tracks finding ID globally.
|
|
20
|
-
// Return IDs as-is to preserve compatibility where only membership checks are used.
|
|
21
|
-
return findingIds;
|
|
22
|
-
}
|
package/mcp/dist/shared-paths.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./phren-paths.js";
|