@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/cli-config.js
CHANGED
|
@@ -1,17 +1,132 @@
|
|
|
1
1
|
import { getPhrenPath, readRootManifest } from "./shared.js";
|
|
2
2
|
import { installPreferencesFile } from "./phren-paths.js";
|
|
3
|
-
import { getIndexPolicy, updateIndexPolicy, getRetentionPolicy, updateRetentionPolicy, getWorkflowPolicy, updateWorkflowPolicy, } from "./shared-governance.js";
|
|
4
|
-
import { listMachines as listMachinesStore, listProfiles as listProfilesStore } from "./data-access.js";
|
|
3
|
+
import { getIndexPolicy, updateIndexPolicy, getRetentionPolicy, updateRetentionPolicy, getWorkflowPolicy, updateWorkflowPolicy, mergeConfig, VALID_TASK_MODES, VALID_FINDING_SENSITIVITY, } from "./shared-governance.js";
|
|
4
|
+
import { listMachines as listMachinesStore, listProfiles as listProfilesStore, listProfiles } from "./data-access.js";
|
|
5
5
|
import { setTelemetryEnabled, getTelemetrySummary, resetTelemetry } from "./telemetry.js";
|
|
6
6
|
import { governanceInstallPreferencesFile, readInstallPreferences, readGovernanceInstallPreferences, writeInstallPreferences, writeGovernanceInstallPreferences, } from "./init-preferences.js";
|
|
7
7
|
import { PROACTIVITY_LEVELS, getProactivityLevel, getProactivityLevelForTask, getProactivityLevelForFindings, } from "./proactivity.js";
|
|
8
|
-
import { PROJECT_OWNERSHIP_MODES, getProjectOwnershipDefault, parseProjectOwnershipMode, } from "./project-config.js";
|
|
8
|
+
import { PROJECT_OWNERSHIP_MODES, getProjectOwnershipDefault, parseProjectOwnershipMode, updateProjectConfigOverrides, } from "./project-config.js";
|
|
9
9
|
import { isValidProjectName, learnedSynonymsPath, learnSynonym, loadLearnedSynonyms, removeLearnedSynonym, } from "./utils.js";
|
|
10
|
+
// ── Shared helpers ────────────────────────────────────────────────────────────
|
|
11
|
+
export function parseProjectArg(args) {
|
|
12
|
+
const project = args.find((a) => a.startsWith("--project="))?.slice("--project=".length)
|
|
13
|
+
?? (args.indexOf("--project") !== -1 ? args[args.indexOf("--project") + 1] : undefined);
|
|
14
|
+
const rest = args.filter((a, i) => a !== "--project" && !a.startsWith("--project=") && args[i - 1] !== "--project");
|
|
15
|
+
return { project, rest };
|
|
16
|
+
}
|
|
17
|
+
export function checkProjectInProfile(phrenPath, project) {
|
|
18
|
+
const profiles = listProfiles(phrenPath);
|
|
19
|
+
if (profiles.ok) {
|
|
20
|
+
const registered = profiles.data.some((entry) => entry.projects.includes(project));
|
|
21
|
+
if (!registered) {
|
|
22
|
+
return `Warning: Project '${project}' not found in active profile. Run 'phren add /path/to/${project}' first.\n Config was written to ${phrenPath}/${project}/phren.project.yaml but won't be used until the project is registered.`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function warnIfUnregistered(phrenPath, project) {
|
|
28
|
+
const warning = checkProjectInProfile(phrenPath, project);
|
|
29
|
+
if (warning)
|
|
30
|
+
console.error(warning);
|
|
31
|
+
}
|
|
32
|
+
export function buildProactivitySnapshot(phrenPath) {
|
|
33
|
+
const prefs = readGovernanceInstallPreferences(phrenPath);
|
|
34
|
+
return {
|
|
35
|
+
path: governanceInstallPreferencesFile(phrenPath),
|
|
36
|
+
configured: {
|
|
37
|
+
proactivity: prefs.proactivity ?? null,
|
|
38
|
+
proactivityFindings: prefs.proactivityFindings ?? null,
|
|
39
|
+
proactivityTask: prefs.proactivityTask ?? null,
|
|
40
|
+
},
|
|
41
|
+
effective: {
|
|
42
|
+
proactivity: getProactivityLevel(phrenPath),
|
|
43
|
+
proactivityFindings: getProactivityLevelForFindings(phrenPath),
|
|
44
|
+
proactivityTask: getProactivityLevelForTask(phrenPath),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// ── Config show ───────────────────────────────────────────────────────────────
|
|
49
|
+
function formatConfigAsTable(label, rows) {
|
|
50
|
+
const maxKey = Math.max(...rows.map(([k]) => k.length));
|
|
51
|
+
console.log(`\n${label}`);
|
|
52
|
+
for (const [k, v] of rows) {
|
|
53
|
+
console.log(` ${k.padEnd(maxKey)} ${v}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function handleConfigShow(args) {
|
|
57
|
+
const phrenPath = getPhrenPath();
|
|
58
|
+
const { project: projectArg } = parseProjectArg(args);
|
|
59
|
+
if (projectArg) {
|
|
60
|
+
if (!isValidProjectName(projectArg)) {
|
|
61
|
+
console.error(`Invalid project name: "${projectArg}"`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const resolved = mergeConfig(phrenPath, projectArg);
|
|
65
|
+
const inProfile = checkProjectInProfile(phrenPath, projectArg);
|
|
66
|
+
console.log(`\nConfig for project: ${projectArg}${inProfile ? "" : " (not in active profile)"}`);
|
|
67
|
+
formatConfigAsTable("Finding capture", [
|
|
68
|
+
["sensitivity", resolved.findingSensitivity],
|
|
69
|
+
["proactivity", resolved.proactivity.base ?? "(global)"],
|
|
70
|
+
["proactivity.findings", resolved.proactivity.findings ?? "(global)"],
|
|
71
|
+
["proactivity.tasks", resolved.proactivity.tasks ?? "(global)"],
|
|
72
|
+
]);
|
|
73
|
+
formatConfigAsTable("Task automation", [
|
|
74
|
+
["taskMode", resolved.taskMode],
|
|
75
|
+
]);
|
|
76
|
+
formatConfigAsTable("Retention policy", [
|
|
77
|
+
["ttlDays", String(resolved.retentionPolicy.ttlDays)],
|
|
78
|
+
["retentionDays", String(resolved.retentionPolicy.retentionDays)],
|
|
79
|
+
["autoAcceptThreshold", String(resolved.retentionPolicy.autoAcceptThreshold)],
|
|
80
|
+
["minInjectConfidence", String(resolved.retentionPolicy.minInjectConfidence)],
|
|
81
|
+
["decay.d30", String(resolved.retentionPolicy.decay.d30)],
|
|
82
|
+
["decay.d60", String(resolved.retentionPolicy.decay.d60)],
|
|
83
|
+
["decay.d90", String(resolved.retentionPolicy.decay.d90)],
|
|
84
|
+
["decay.d120", String(resolved.retentionPolicy.decay.d120)],
|
|
85
|
+
]);
|
|
86
|
+
formatConfigAsTable("Workflow policy", [
|
|
87
|
+
["lowConfidenceThreshold", String(resolved.workflowPolicy.lowConfidenceThreshold)],
|
|
88
|
+
["riskySections", resolved.workflowPolicy.riskySections.join(", ")],
|
|
89
|
+
]);
|
|
90
|
+
if (!inProfile) {
|
|
91
|
+
console.error(`\nRun 'phren add /path/to/${projectArg}' to register this project.`);
|
|
92
|
+
}
|
|
93
|
+
console.log("");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Global config — no project
|
|
97
|
+
const retention = getRetentionPolicy(phrenPath);
|
|
98
|
+
const workflow = getWorkflowPolicy(phrenPath);
|
|
99
|
+
console.log("\nGlobal config (applies to all projects unless overridden)");
|
|
100
|
+
formatConfigAsTable("Finding capture", [
|
|
101
|
+
["findingSensitivity", workflow.findingSensitivity],
|
|
102
|
+
]);
|
|
103
|
+
formatConfigAsTable("Task automation", [
|
|
104
|
+
["taskMode", workflow.taskMode],
|
|
105
|
+
]);
|
|
106
|
+
formatConfigAsTable("Retention policy", [
|
|
107
|
+
["ttlDays", String(retention.ttlDays)],
|
|
108
|
+
["retentionDays", String(retention.retentionDays)],
|
|
109
|
+
["autoAcceptThreshold", String(retention.autoAcceptThreshold)],
|
|
110
|
+
["minInjectConfidence", String(retention.minInjectConfidence)],
|
|
111
|
+
["decay.d30", String(retention.decay.d30)],
|
|
112
|
+
["decay.d60", String(retention.decay.d60)],
|
|
113
|
+
["decay.d90", String(retention.decay.d90)],
|
|
114
|
+
["decay.d120", String(retention.decay.d120)],
|
|
115
|
+
]);
|
|
116
|
+
formatConfigAsTable("Workflow policy", [
|
|
117
|
+
["lowConfidenceThreshold", String(workflow.lowConfidenceThreshold)],
|
|
118
|
+
["riskySections", workflow.riskySections.join(", ")],
|
|
119
|
+
]);
|
|
120
|
+
console.log(`\nUse '--project <name>' to see merged config for a specific project.`);
|
|
121
|
+
console.log("");
|
|
122
|
+
}
|
|
10
123
|
// ── Config router ────────────────────────────────────────────────────────────
|
|
11
124
|
export async function handleConfig(args) {
|
|
12
125
|
const sub = args[0];
|
|
13
126
|
const rest = args.slice(1);
|
|
14
127
|
switch (sub) {
|
|
128
|
+
case "show":
|
|
129
|
+
return handleConfigShow(rest);
|
|
15
130
|
case "policy":
|
|
16
131
|
return handleRetentionPolicy(rest);
|
|
17
132
|
case "workflow":
|
|
@@ -42,6 +157,7 @@ export async function handleConfig(args) {
|
|
|
42
157
|
console.log(`phren config - manage settings and policies
|
|
43
158
|
|
|
44
159
|
Subcommands:
|
|
160
|
+
phren config show [--project <name>] Full merged config summary (what's actually active)
|
|
45
161
|
phren config policy [get|set ...] Memory retention, TTL, confidence, decay
|
|
46
162
|
phren config workflow [get|set ...] Risky-memory thresholds, task automation mode
|
|
47
163
|
phren config index [get|set ...] Indexer include/exclude globs
|
|
@@ -137,30 +253,29 @@ function handleConfigSynonyms(args) {
|
|
|
137
253
|
printSynonymsUsage();
|
|
138
254
|
process.exit(1);
|
|
139
255
|
}
|
|
140
|
-
function proactivityConfigSnapshot(phrenPath) {
|
|
141
|
-
const prefs = readGovernanceInstallPreferences(phrenPath);
|
|
142
|
-
return {
|
|
143
|
-
path: governanceInstallPreferencesFile(phrenPath),
|
|
144
|
-
configured: {
|
|
145
|
-
proactivity: prefs.proactivity ?? null,
|
|
146
|
-
proactivityFindings: prefs.proactivityFindings ?? null,
|
|
147
|
-
proactivityTask: prefs.proactivityTask ?? null,
|
|
148
|
-
},
|
|
149
|
-
effective: {
|
|
150
|
-
proactivity: getProactivityLevel(phrenPath),
|
|
151
|
-
proactivityFindings: getProactivityLevelForFindings(phrenPath),
|
|
152
|
-
proactivityTask: getProactivityLevelForTask(phrenPath),
|
|
153
|
-
},
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
256
|
function handleConfigProactivity(subcommand, args) {
|
|
157
257
|
const phrenPath = getPhrenPath();
|
|
158
|
-
const
|
|
258
|
+
const { project: projectArg, rest: filteredArgs } = parseProjectArg(args);
|
|
259
|
+
const value = filteredArgs[0];
|
|
159
260
|
if (value === undefined) {
|
|
160
|
-
|
|
261
|
+
if (projectArg) {
|
|
262
|
+
if (!isValidProjectName(projectArg)) {
|
|
263
|
+
console.error(`Invalid project name: "${projectArg}"`);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
const resolved = mergeConfig(phrenPath, projectArg);
|
|
267
|
+
console.log(JSON.stringify({
|
|
268
|
+
_project: projectArg,
|
|
269
|
+
base: resolved.proactivity.base ?? null,
|
|
270
|
+
findings: resolved.proactivity.findings ?? null,
|
|
271
|
+
tasks: resolved.proactivity.tasks ?? null,
|
|
272
|
+
}, null, 2));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
console.log(JSON.stringify(buildProactivitySnapshot(phrenPath), null, 2));
|
|
161
276
|
return;
|
|
162
277
|
}
|
|
163
|
-
if (
|
|
278
|
+
if (filteredArgs.length !== 1) {
|
|
164
279
|
printProactivityUsage(subcommand);
|
|
165
280
|
process.exit(1);
|
|
166
281
|
}
|
|
@@ -169,6 +284,25 @@ function handleConfigProactivity(subcommand, args) {
|
|
|
169
284
|
printProactivityUsage(subcommand);
|
|
170
285
|
process.exit(1);
|
|
171
286
|
}
|
|
287
|
+
if (projectArg) {
|
|
288
|
+
if (!isValidProjectName(projectArg)) {
|
|
289
|
+
console.error(`Invalid project name: "${projectArg}"`);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
warnIfUnregistered(phrenPath, projectArg);
|
|
293
|
+
const key = subcommand === "proactivity" ? "proactivity"
|
|
294
|
+
: subcommand === "proactivity.findings" ? "proactivityFindings"
|
|
295
|
+
: "proactivityTask";
|
|
296
|
+
updateProjectConfigOverrides(phrenPath, projectArg, (current) => ({ ...current, [key]: level }));
|
|
297
|
+
const resolved = mergeConfig(phrenPath, projectArg);
|
|
298
|
+
console.log(JSON.stringify({
|
|
299
|
+
_project: projectArg,
|
|
300
|
+
base: resolved.proactivity.base ?? null,
|
|
301
|
+
findings: resolved.proactivity.findings ?? null,
|
|
302
|
+
tasks: resolved.proactivity.tasks ?? null,
|
|
303
|
+
}, null, 2));
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
172
306
|
switch (subcommand) {
|
|
173
307
|
case "proactivity":
|
|
174
308
|
writeGovernanceInstallPreferences(phrenPath, { proactivity: level });
|
|
@@ -180,7 +314,7 @@ function handleConfigProactivity(subcommand, args) {
|
|
|
180
314
|
writeGovernanceInstallPreferences(phrenPath, { proactivityTask: level });
|
|
181
315
|
break;
|
|
182
316
|
}
|
|
183
|
-
console.log(JSON.stringify(
|
|
317
|
+
console.log(JSON.stringify(buildProactivitySnapshot(phrenPath), null, 2));
|
|
184
318
|
}
|
|
185
319
|
function projectOwnershipConfigSnapshot(phrenPath) {
|
|
186
320
|
const prefs = readInstallPreferences(phrenPath);
|
|
@@ -213,61 +347,7 @@ function handleConfigProjectOwnership(args) {
|
|
|
213
347
|
writeInstallPreferences(phrenPath, { projectOwnershipDefault: ownership });
|
|
214
348
|
console.log(JSON.stringify(projectOwnershipConfigSnapshot(phrenPath), null, 2));
|
|
215
349
|
}
|
|
216
|
-
// ──
|
|
217
|
-
const TASK_MODES = ["off", "manual", "suggest", "auto"];
|
|
218
|
-
function normalizeTaskMode(raw) {
|
|
219
|
-
if (!raw)
|
|
220
|
-
return undefined;
|
|
221
|
-
const normalized = raw.trim().toLowerCase();
|
|
222
|
-
return TASK_MODES.includes(normalized) ? normalized : undefined;
|
|
223
|
-
}
|
|
224
|
-
function taskModeConfigSnapshot(phrenPath) {
|
|
225
|
-
const policy = getWorkflowPolicy(phrenPath);
|
|
226
|
-
return {
|
|
227
|
-
taskMode: policy.taskMode,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
function handleConfigTaskMode(args) {
|
|
231
|
-
const phrenPath = getPhrenPath();
|
|
232
|
-
const action = args[0];
|
|
233
|
-
if (!action || action === "get") {
|
|
234
|
-
console.log(JSON.stringify(taskModeConfigSnapshot(phrenPath), null, 2));
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (action === "set") {
|
|
238
|
-
const mode = normalizeTaskMode(args[1]);
|
|
239
|
-
if (!mode) {
|
|
240
|
-
console.error(`Usage: phren config task-mode set [${TASK_MODES.join("|")}]`);
|
|
241
|
-
process.exit(1);
|
|
242
|
-
}
|
|
243
|
-
const result = updateWorkflowPolicy(phrenPath, { taskMode: mode });
|
|
244
|
-
if (!result.ok) {
|
|
245
|
-
console.error(result.error);
|
|
246
|
-
if (result.code === "PERMISSION_DENIED")
|
|
247
|
-
process.exit(1);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
console.log(JSON.stringify(taskModeConfigSnapshot(phrenPath), null, 2));
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
// Bare value: phren config task-mode auto
|
|
254
|
-
const mode = normalizeTaskMode(action);
|
|
255
|
-
if (mode) {
|
|
256
|
-
const result = updateWorkflowPolicy(phrenPath, { taskMode: mode });
|
|
257
|
-
if (!result.ok) {
|
|
258
|
-
console.error(result.error);
|
|
259
|
-
if (result.code === "PERMISSION_DENIED")
|
|
260
|
-
process.exit(1);
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
console.log(JSON.stringify(taskModeConfigSnapshot(phrenPath), null, 2));
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
console.error(`Usage: phren config task-mode [get|set <mode>|<mode>] — modes: ${TASK_MODES.join("|")}`);
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
// ── Finding sensitivity ───────────────────────────────────────────────────────
|
|
270
|
-
const FINDING_SENSITIVITY_LEVELS = ["minimal", "conservative", "balanced", "aggressive"];
|
|
350
|
+
// ── Finding sensitivity config ────────────────────────────────────────────────
|
|
271
351
|
export const FINDING_SENSITIVITY_CONFIG = {
|
|
272
352
|
minimal: {
|
|
273
353
|
sessionCap: 0,
|
|
@@ -290,59 +370,94 @@ export const FINDING_SENSITIVITY_CONFIG = {
|
|
|
290
370
|
agentInstruction: "Save everything worth remembering — err on the side of capturing.",
|
|
291
371
|
},
|
|
292
372
|
};
|
|
293
|
-
function
|
|
294
|
-
if (!v)
|
|
295
|
-
return null;
|
|
296
|
-
const lower = v.toLowerCase();
|
|
297
|
-
if (FINDING_SENSITIVITY_LEVELS.includes(lower))
|
|
298
|
-
return lower;
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
|
-
function findingSensitivityConfigSnapshot(phrenPath) {
|
|
302
|
-
const policy = getWorkflowPolicy(phrenPath);
|
|
303
|
-
const level = policy.findingSensitivity;
|
|
304
|
-
const config = FINDING_SENSITIVITY_CONFIG[level];
|
|
305
|
-
return { level, ...config };
|
|
306
|
-
}
|
|
307
|
-
function handleConfigFindingSensitivity(args) {
|
|
373
|
+
function handleWorkflowField(args, opts) {
|
|
308
374
|
const phrenPath = getPhrenPath();
|
|
309
|
-
const
|
|
375
|
+
const { project: projectArg, rest: filteredArgs } = parseProjectArg(args);
|
|
376
|
+
const action = filteredArgs[0];
|
|
310
377
|
if (!action || action === "get") {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (action === "set") {
|
|
315
|
-
const level = normalizeFindingSensitivity(args[1]);
|
|
316
|
-
if (!level) {
|
|
317
|
-
console.error(`Usage: phren config finding-sensitivity set [${FINDING_SENSITIVITY_LEVELS.join("|")}]`);
|
|
318
|
-
process.exit(1);
|
|
319
|
-
}
|
|
320
|
-
const result = updateWorkflowPolicy(phrenPath, { findingSensitivity: level });
|
|
321
|
-
if (!result.ok) {
|
|
322
|
-
console.error(result.error);
|
|
323
|
-
if (result.code === "PERMISSION_DENIED")
|
|
378
|
+
if (projectArg) {
|
|
379
|
+
if (!isValidProjectName(projectArg)) {
|
|
380
|
+
console.error(`Invalid project name: "${projectArg}"`);
|
|
324
381
|
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
const resolved = mergeConfig(phrenPath, projectArg);
|
|
384
|
+
const value = opts.getProjectValue(resolved);
|
|
385
|
+
console.log(JSON.stringify(opts.formatProjectOutput(projectArg, value), null, 2));
|
|
325
386
|
return;
|
|
326
387
|
}
|
|
327
|
-
console.log(JSON.stringify(
|
|
388
|
+
console.log(JSON.stringify(opts.getSnapshot(phrenPath), null, 2));
|
|
328
389
|
return;
|
|
329
390
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
391
|
+
const applyValue = (value) => {
|
|
392
|
+
if (projectArg) {
|
|
393
|
+
if (!isValidProjectName(projectArg)) {
|
|
394
|
+
console.error(`Invalid project name: "${projectArg}"`);
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
warnIfUnregistered(phrenPath, projectArg);
|
|
398
|
+
updateProjectConfigOverrides(phrenPath, projectArg, (current) => ({ ...current, [opts.projectOverrideKey]: value }));
|
|
399
|
+
const resolved = mergeConfig(phrenPath, projectArg);
|
|
400
|
+
const eff = opts.getProjectValue(resolved);
|
|
401
|
+
console.log(JSON.stringify(opts.formatProjectOutput(projectArg, eff), null, 2));
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const result = updateWorkflowPolicy(phrenPath, { [opts.workflowPatchKey]: value });
|
|
334
405
|
if (!result.ok) {
|
|
335
406
|
console.error(result.error);
|
|
336
407
|
if (result.code === "PERMISSION_DENIED")
|
|
337
408
|
process.exit(1);
|
|
338
409
|
return;
|
|
339
410
|
}
|
|
340
|
-
console.log(JSON.stringify(
|
|
341
|
-
|
|
411
|
+
console.log(JSON.stringify(opts.getSnapshot(phrenPath), null, 2));
|
|
412
|
+
};
|
|
413
|
+
if (action === "set") {
|
|
414
|
+
const value = opts.normalize(filteredArgs[1]);
|
|
415
|
+
if (!value) {
|
|
416
|
+
console.error(`Usage: phren config ${opts.fieldName} set [${opts.validValues.join("|")}]`);
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
return applyValue(value);
|
|
342
420
|
}
|
|
343
|
-
|
|
421
|
+
const value = opts.normalize(action);
|
|
422
|
+
if (value)
|
|
423
|
+
return applyValue(value);
|
|
424
|
+
console.error(`Usage: phren config ${opts.fieldName} [--project <name>] [get|set <value>|<value>] — values: ${opts.validValues.join("|")}`);
|
|
344
425
|
process.exit(1);
|
|
345
426
|
}
|
|
427
|
+
function normalizeFromList(raw, validValues) {
|
|
428
|
+
if (!raw)
|
|
429
|
+
return null;
|
|
430
|
+
const lower = raw.trim().toLowerCase();
|
|
431
|
+
return validValues.includes(lower) ? lower : null;
|
|
432
|
+
}
|
|
433
|
+
function handleConfigTaskMode(args) {
|
|
434
|
+
handleWorkflowField(args, {
|
|
435
|
+
fieldName: "task-mode",
|
|
436
|
+
validValues: VALID_TASK_MODES,
|
|
437
|
+
normalize: (raw) => normalizeFromList(raw, VALID_TASK_MODES),
|
|
438
|
+
getSnapshot: (phrenPath) => ({ taskMode: getWorkflowPolicy(phrenPath).taskMode }),
|
|
439
|
+
getProjectValue: (resolved) => resolved.taskMode,
|
|
440
|
+
formatProjectOutput: (proj, value) => ({ _project: proj, taskMode: value }),
|
|
441
|
+
workflowPatchKey: "taskMode",
|
|
442
|
+
projectOverrideKey: "taskMode",
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
function handleConfigFindingSensitivity(args) {
|
|
446
|
+
handleWorkflowField(args, {
|
|
447
|
+
fieldName: "finding-sensitivity",
|
|
448
|
+
validValues: VALID_FINDING_SENSITIVITY,
|
|
449
|
+
normalize: (raw) => normalizeFromList(raw, VALID_FINDING_SENSITIVITY),
|
|
450
|
+
getSnapshot: (phrenPath) => {
|
|
451
|
+
const policy = getWorkflowPolicy(phrenPath);
|
|
452
|
+
const level = policy.findingSensitivity;
|
|
453
|
+
return { level, ...FINDING_SENSITIVITY_CONFIG[level] };
|
|
454
|
+
},
|
|
455
|
+
getProjectValue: (resolved) => resolved.findingSensitivity,
|
|
456
|
+
formatProjectOutput: (proj, value) => ({ _project: proj, level: value, ...FINDING_SENSITIVITY_CONFIG[value] }),
|
|
457
|
+
workflowPatchKey: "findingSensitivity",
|
|
458
|
+
projectOverrideKey: "findingSensitivity",
|
|
459
|
+
});
|
|
460
|
+
}
|
|
346
461
|
// ── LLM config ───────────────────────────────────────────────────────────────
|
|
347
462
|
const EXPENSIVE_MODEL_RE = /opus|sonnet|gpt-4(?!o-mini)/i;
|
|
348
463
|
const DEFAULT_LLM_MODEL = "gpt-4o-mini / claude-haiku-4-5-20251001";
|
|
@@ -429,7 +544,7 @@ export async function handleIndexPolicy(args) {
|
|
|
429
544
|
}
|
|
430
545
|
const result = updateIndexPolicy(getPhrenPath(), patch);
|
|
431
546
|
if (!result.ok) {
|
|
432
|
-
console.
|
|
547
|
+
console.error(result.error);
|
|
433
548
|
if (result.code === "PERMISSION_DENIED")
|
|
434
549
|
process.exit(1);
|
|
435
550
|
return;
|
|
@@ -442,10 +557,20 @@ export async function handleIndexPolicy(args) {
|
|
|
442
557
|
}
|
|
443
558
|
// ── Memory policy ────────────────────────────────────────────────────────────
|
|
444
559
|
export async function handleRetentionPolicy(args) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
560
|
+
const phrenPath = getPhrenPath();
|
|
561
|
+
const { project: projectArg, rest: filteredArgs } = parseProjectArg(args);
|
|
562
|
+
if (!filteredArgs.length || filteredArgs[0] === "get") {
|
|
563
|
+
if (projectArg) {
|
|
564
|
+
if (!isValidProjectName(projectArg)) {
|
|
565
|
+
console.error(`Invalid project name: "${projectArg}"`);
|
|
566
|
+
process.exit(1);
|
|
567
|
+
}
|
|
568
|
+
const resolved = mergeConfig(phrenPath, projectArg);
|
|
569
|
+
console.log(JSON.stringify({ _project: projectArg, ...resolved.retentionPolicy }, null, 2));
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
console.log(JSON.stringify(getRetentionPolicy(phrenPath), null, 2));
|
|
573
|
+
const conflictOn = process.env.PHREN_FEATURE_SEMANTIC_CONFLICT === "1";
|
|
449
574
|
process.stderr.write(`\nDedup: free Jaccard similarity scan on every add_finding (no API key needed).\n`);
|
|
450
575
|
process.stderr.write(` Near-matches (30–55% overlap) are returned in the response for the agent to decide.\n`);
|
|
451
576
|
if (conflictOn) {
|
|
@@ -458,9 +583,40 @@ export async function handleRetentionPolicy(args) {
|
|
|
458
583
|
}
|
|
459
584
|
return;
|
|
460
585
|
}
|
|
461
|
-
if (
|
|
586
|
+
if (filteredArgs[0] === "set") {
|
|
587
|
+
if (projectArg) {
|
|
588
|
+
if (!isValidProjectName(projectArg)) {
|
|
589
|
+
console.error(`Invalid project name: "${projectArg}"`);
|
|
590
|
+
process.exit(1);
|
|
591
|
+
}
|
|
592
|
+
warnIfUnregistered(phrenPath, projectArg);
|
|
593
|
+
updateProjectConfigOverrides(phrenPath, projectArg, (current) => {
|
|
594
|
+
const existingRetention = current.retentionPolicy ?? {};
|
|
595
|
+
const retentionPatch = { ...existingRetention };
|
|
596
|
+
for (const arg of filteredArgs.slice(1)) {
|
|
597
|
+
if (!arg.startsWith("--"))
|
|
598
|
+
continue;
|
|
599
|
+
const [k, v] = arg.slice(2).split("=");
|
|
600
|
+
if (!k || v === undefined)
|
|
601
|
+
continue;
|
|
602
|
+
const num = Number(v);
|
|
603
|
+
const value = Number.isNaN(num) ? v : num;
|
|
604
|
+
if (k.startsWith("decay.")) {
|
|
605
|
+
retentionPatch.decay = { ...(retentionPatch.decay ?? {}) };
|
|
606
|
+
retentionPatch.decay[k.slice("decay.".length)] = value;
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
retentionPatch[k] = value;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return { ...current, retentionPolicy: retentionPatch };
|
|
613
|
+
});
|
|
614
|
+
const resolved = mergeConfig(phrenPath, projectArg);
|
|
615
|
+
console.log(JSON.stringify({ _project: projectArg, ...resolved.retentionPolicy }, null, 2));
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
462
618
|
const patch = {};
|
|
463
|
-
for (const arg of
|
|
619
|
+
for (const arg of filteredArgs.slice(1)) {
|
|
464
620
|
if (!arg.startsWith("--"))
|
|
465
621
|
continue;
|
|
466
622
|
const [k, v] = arg.slice(2).split("=");
|
|
@@ -476,9 +632,9 @@ export async function handleRetentionPolicy(args) {
|
|
|
476
632
|
patch[k] = value;
|
|
477
633
|
}
|
|
478
634
|
}
|
|
479
|
-
const result = updateRetentionPolicy(
|
|
635
|
+
const result = updateRetentionPolicy(phrenPath, patch);
|
|
480
636
|
if (!result.ok) {
|
|
481
|
-
console.
|
|
637
|
+
console.error(result.error);
|
|
482
638
|
if (result.code === "PERMISSION_DENIED")
|
|
483
639
|
process.exit(1);
|
|
484
640
|
return;
|
|
@@ -486,18 +642,68 @@ export async function handleRetentionPolicy(args) {
|
|
|
486
642
|
console.log(JSON.stringify(result.data, null, 2));
|
|
487
643
|
return;
|
|
488
644
|
}
|
|
489
|
-
console.error("Usage: phren config policy [get|set --ttlDays=120 --retentionDays=365 --autoAcceptThreshold=0.75 --minInjectConfidence=0.35 --decay.d30=1 --decay.d60=0.85 --decay.d90=0.65 --decay.d120=0.45]");
|
|
645
|
+
console.error("Usage: phren config policy [--project <name>] [get|set --ttlDays=120 --retentionDays=365 --autoAcceptThreshold=0.75 --minInjectConfidence=0.35 --decay.d30=1 --decay.d60=0.85 --decay.d90=0.65 --decay.d120=0.45]");
|
|
490
646
|
process.exit(1);
|
|
491
647
|
}
|
|
492
648
|
// ── Memory workflow ──────────────────────────────────────────────────────────
|
|
493
649
|
export async function handleWorkflowPolicy(args) {
|
|
494
|
-
|
|
495
|
-
|
|
650
|
+
const phrenPath = getPhrenPath();
|
|
651
|
+
const { project: projectArg, rest: filteredArgs } = parseProjectArg(args);
|
|
652
|
+
if (!filteredArgs.length || filteredArgs[0] === "get") {
|
|
653
|
+
if (projectArg) {
|
|
654
|
+
if (!isValidProjectName(projectArg)) {
|
|
655
|
+
console.error(`Invalid project name: "${projectArg}"`);
|
|
656
|
+
process.exit(1);
|
|
657
|
+
}
|
|
658
|
+
const resolved = mergeConfig(phrenPath, projectArg);
|
|
659
|
+
console.log(JSON.stringify({ _project: projectArg, ...resolved.workflowPolicy }, null, 2));
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
console.log(JSON.stringify(getWorkflowPolicy(phrenPath), null, 2));
|
|
496
663
|
return;
|
|
497
664
|
}
|
|
498
|
-
if (
|
|
665
|
+
if (filteredArgs[0] === "set") {
|
|
666
|
+
if (projectArg) {
|
|
667
|
+
if (!isValidProjectName(projectArg)) {
|
|
668
|
+
console.error(`Invalid project name: "${projectArg}"`);
|
|
669
|
+
process.exit(1);
|
|
670
|
+
}
|
|
671
|
+
warnIfUnregistered(phrenPath, projectArg);
|
|
672
|
+
updateProjectConfigOverrides(phrenPath, projectArg, (current) => {
|
|
673
|
+
const nextConfig = { ...current };
|
|
674
|
+
const existingWorkflow = current.workflowPolicy ?? {};
|
|
675
|
+
const workflowPatch = { ...existingWorkflow };
|
|
676
|
+
for (const arg of filteredArgs.slice(1)) {
|
|
677
|
+
if (!arg.startsWith("--"))
|
|
678
|
+
continue;
|
|
679
|
+
const [k, v] = arg.slice(2).split("=");
|
|
680
|
+
if (!k || v === undefined)
|
|
681
|
+
continue;
|
|
682
|
+
if (k === "riskySections") {
|
|
683
|
+
workflowPatch.riskySections = v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
684
|
+
}
|
|
685
|
+
else if (k === "taskMode") {
|
|
686
|
+
nextConfig.taskMode = v;
|
|
687
|
+
}
|
|
688
|
+
else if (k === "findingSensitivity") {
|
|
689
|
+
nextConfig.findingSensitivity = v;
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
const num = Number(v);
|
|
693
|
+
workflowPatch[k] = Number.isNaN(num) ? v : num;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (Object.keys(workflowPatch).length > 0 || existingWorkflow !== current.workflowPolicy) {
|
|
697
|
+
nextConfig.workflowPolicy = workflowPatch;
|
|
698
|
+
}
|
|
699
|
+
return nextConfig;
|
|
700
|
+
});
|
|
701
|
+
const resolved = mergeConfig(phrenPath, projectArg);
|
|
702
|
+
console.log(JSON.stringify({ _project: projectArg, ...resolved.workflowPolicy }, null, 2));
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
499
705
|
const patch = {};
|
|
500
|
-
for (const arg of
|
|
706
|
+
for (const arg of filteredArgs.slice(1)) {
|
|
501
707
|
if (!arg.startsWith("--"))
|
|
502
708
|
continue;
|
|
503
709
|
const [k, v] = arg.slice(2).split("=");
|
|
@@ -511,7 +717,7 @@ export async function handleWorkflowPolicy(args) {
|
|
|
511
717
|
patch[k] = Number.isNaN(num) ? v : num;
|
|
512
718
|
}
|
|
513
719
|
}
|
|
514
|
-
const result = updateWorkflowPolicy(
|
|
720
|
+
const result = updateWorkflowPolicy(phrenPath, patch);
|
|
515
721
|
if (!result.ok) {
|
|
516
722
|
console.log(result.error);
|
|
517
723
|
if (result.code === "PERMISSION_DENIED")
|
|
@@ -521,7 +727,7 @@ export async function handleWorkflowPolicy(args) {
|
|
|
521
727
|
console.log(JSON.stringify(result.data, null, 2));
|
|
522
728
|
return;
|
|
523
729
|
}
|
|
524
|
-
console.error("Usage: phren config workflow [get|set --lowConfidenceThreshold=0.7 --riskySections=Stale,Conflicts --taskMode=manual]");
|
|
730
|
+
console.error("Usage: phren config workflow [--project <name>] [get|set --lowConfidenceThreshold=0.7 --riskySections=Stale,Conflicts --taskMode=manual]");
|
|
525
731
|
process.exit(1);
|
|
526
732
|
}
|
|
527
733
|
// ── Machines and profiles ────────────────────────────────────────────────────
|
package/mcp/dist/cli-extract.js
CHANGED
|
@@ -240,9 +240,10 @@ export async function handleExtractMemories(projectArg, cwdArg, silent = false,
|
|
|
240
240
|
console.log(`Skipped memory extraction for ${project}: findings proactivity is low.`);
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
243
|
-
const
|
|
243
|
+
const rawDays = Number.parseInt((process.env.PHREN_MEMORY_EXTRACT_WINDOW_DAYS) || "30", 10);
|
|
244
|
+
const days = Number.isNaN(rawDays) ? 30 : Math.max(1, rawDays);
|
|
244
245
|
const threshold = Number.parseFloat((process.env.PHREN_MEMORY_AUTO_ACCEPT) || String(getRetentionPolicy(getPhrenPath()).autoAcceptThreshold));
|
|
245
|
-
const records = parseGitLogRecords(repoRoot,
|
|
246
|
+
const records = parseGitLogRecords(repoRoot, days);
|
|
246
247
|
const ghCandidates = isFeatureEnabled("PHREN_FEATURE_GH_MINING", false)
|
|
247
248
|
? await mineGithubCandidates(repoRoot)
|
|
248
249
|
: [];
|