@phren/cli 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -8
- package/mcp/dist/cli-actions.js +5 -5
- package/mcp/dist/cli-config.js +334 -127
- package/mcp/dist/cli-govern.js +140 -3
- package/mcp/dist/cli-graph.js +3 -2
- package/mcp/dist/cli-hooks-globs.js +2 -1
- package/mcp/dist/cli-hooks-output.js +3 -3
- package/mcp/dist/cli-hooks.js +41 -34
- package/mcp/dist/cli-namespaces.js +15 -5
- package/mcp/dist/cli-search.js +2 -2
- package/mcp/dist/content-archive.js +2 -2
- package/mcp/dist/content-citation.js +12 -22
- package/mcp/dist/content-dedup.js +9 -9
- package/mcp/dist/data-access.js +1 -1
- package/mcp/dist/data-tasks.js +23 -0
- package/mcp/dist/embedding.js +7 -7
- package/mcp/dist/entrypoint.js +129 -102
- package/mcp/dist/governance-locks.js +6 -5
- package/mcp/dist/governance-policy.js +155 -2
- package/mcp/dist/governance-scores.js +3 -3
- package/mcp/dist/hooks.js +39 -18
- package/mcp/dist/index.js +4 -4
- package/mcp/dist/init-config.js +3 -24
- package/mcp/dist/init-setup.js +5 -5
- package/mcp/dist/init.js +170 -23
- package/mcp/dist/link-checksums.js +3 -2
- package/mcp/dist/link-context.js +1 -1
- package/mcp/dist/link-doctor.js +3 -3
- package/mcp/dist/link-skills.js +98 -12
- package/mcp/dist/link.js +17 -27
- package/mcp/dist/machine-identity.js +1 -9
- package/mcp/dist/mcp-config.js +247 -42
- package/mcp/dist/mcp-data.js +9 -9
- package/mcp/dist/mcp-extract-facts.js +1 -1
- package/mcp/dist/mcp-extract.js +2 -2
- package/mcp/dist/mcp-finding.js +6 -6
- package/mcp/dist/mcp-graph.js +11 -11
- package/mcp/dist/mcp-ops.js +18 -18
- package/mcp/dist/mcp-search.js +8 -8
- package/mcp/dist/mcp-tasks.js +21 -1
- package/mcp/dist/memory-ui-page.js +23 -0
- package/mcp/dist/memory-ui-scripts.js +210 -27
- package/mcp/dist/memory-ui-server.js +115 -3
- package/mcp/dist/phren-paths.js +7 -7
- package/mcp/dist/profile-store.js +2 -2
- package/mcp/dist/project-config.js +63 -16
- package/mcp/dist/session-utils.js +3 -2
- package/mcp/dist/shared-fragment-graph.js +22 -21
- package/mcp/dist/shared-index.js +144 -105
- package/mcp/dist/shared-retrieval.js +22 -56
- package/mcp/dist/shared-search-fallback.js +13 -13
- package/mcp/dist/shared-sqljs.js +3 -2
- package/mcp/dist/shared.js +3 -3
- package/mcp/dist/shell-input.js +1 -1
- package/mcp/dist/shell-state-store.js +1 -1
- package/mcp/dist/shell-view.js +3 -2
- package/mcp/dist/shell.js +1 -1
- package/mcp/dist/skill-files.js +4 -10
- package/mcp/dist/skill-registry.js +3 -0
- package/mcp/dist/status.js +41 -13
- package/mcp/dist/task-hygiene.js +1 -1
- package/mcp/dist/telemetry.js +5 -4
- package/mcp/dist/update.js +1 -1
- package/mcp/dist/utils.js +3 -3
- package/package.json +2 -2
- package/starter/global/skills/audit.md +106 -0
- package/mcp/dist/shared-paths.js +0 -1
package/mcp/dist/mcp-config.js
CHANGED
|
@@ -1,27 +1,38 @@
|
|
|
1
1
|
import { mcpResponse } from "./mcp-types.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { getRetentionPolicy, updateRetentionPolicy, getWorkflowPolicy, updateWorkflowPolicy, getIndexPolicy, updateIndexPolicy, } from "./shared-governance.js";
|
|
4
|
-
import { PROACTIVITY_LEVELS,
|
|
5
|
-
import {
|
|
6
|
-
import { FINDING_SENSITIVITY_CONFIG } from "./cli-config.js";
|
|
3
|
+
import { getRetentionPolicy, updateRetentionPolicy, getWorkflowPolicy, updateWorkflowPolicy, getIndexPolicy, updateIndexPolicy, mergeConfig, VALID_TASK_MODES, VALID_FINDING_SENSITIVITY, } from "./shared-governance.js";
|
|
4
|
+
import { PROACTIVITY_LEVELS, } from "./proactivity.js";
|
|
5
|
+
import { writeGovernanceInstallPreferences, } from "./init-preferences.js";
|
|
6
|
+
import { FINDING_SENSITIVITY_CONFIG, buildProactivitySnapshot, checkProjectInProfile } from "./cli-config.js";
|
|
7
|
+
import { readProjectConfig, updateProjectConfigOverrides, } from "./project-config.js";
|
|
8
|
+
import { isValidProjectName } from "./utils.js";
|
|
7
9
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
8
10
|
function proactivitySnapshot(phrenPath) {
|
|
9
|
-
const
|
|
10
|
-
return {
|
|
11
|
-
configured: {
|
|
12
|
-
proactivity: prefs.proactivity ?? null,
|
|
13
|
-
proactivityFindings: prefs.proactivityFindings ?? null,
|
|
14
|
-
proactivityTask: prefs.proactivityTask ?? null,
|
|
15
|
-
},
|
|
16
|
-
effective: {
|
|
17
|
-
proactivity: getProactivityLevel(phrenPath),
|
|
18
|
-
proactivityFindings: getProactivityLevelForFindings(phrenPath),
|
|
19
|
-
proactivityTask: getProactivityLevelForTask(phrenPath),
|
|
20
|
-
},
|
|
21
|
-
};
|
|
11
|
+
const snap = buildProactivitySnapshot(phrenPath);
|
|
12
|
+
return { configured: snap.configured, effective: snap.effective };
|
|
22
13
|
}
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
function validateProject(project) {
|
|
15
|
+
if (!isValidProjectName(project))
|
|
16
|
+
return `Invalid project name: "${project}".`;
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
function checkProjectRegistered(phrenPath, project) {
|
|
20
|
+
const warning = checkProjectInProfile(phrenPath, project);
|
|
21
|
+
if (warning) {
|
|
22
|
+
return `Project '${project}' is not registered in your active profile. Config was written but won't take effect until you run 'phren add' to register the project.`;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function normalizeProjectOverrides(raw) {
|
|
27
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
28
|
+
}
|
|
29
|
+
function getProjectOverrides(phrenPath, project) {
|
|
30
|
+
return normalizeProjectOverrides(readProjectConfig(phrenPath, project).config);
|
|
31
|
+
}
|
|
32
|
+
function hasOwnOverride(overrides, key) {
|
|
33
|
+
return Object.prototype.hasOwnProperty.call(overrides, key);
|
|
34
|
+
}
|
|
35
|
+
const projectParam = z.string().optional().describe("Project name. When provided, writes to that project's phren.project.yaml instead of global .governance/.");
|
|
25
36
|
// ── Registration ────────────────────────────────────────────────────────────
|
|
26
37
|
export function register(server, ctx) {
|
|
27
38
|
const { phrenPath } = ctx;
|
|
@@ -30,15 +41,86 @@ export function register(server, ctx) {
|
|
|
30
41
|
title: "◆ phren · get config",
|
|
31
42
|
description: "Read current configuration for one or all config domains: proactivity, taskMode, " +
|
|
32
43
|
"findingSensitivity, retention (policy), workflow, access, index. " +
|
|
33
|
-
"Returns both configured and effective values."
|
|
44
|
+
"Returns both configured and effective values. When project is provided, returns " +
|
|
45
|
+
"the merged view with project overrides applied and _source annotations.",
|
|
34
46
|
inputSchema: z.object({
|
|
35
47
|
domain: z
|
|
36
48
|
.enum(["proactivity", "taskMode", "findingSensitivity", "retention", "workflow", "access", "index", "all"])
|
|
37
49
|
.optional()
|
|
38
50
|
.describe("Config domain to read. Defaults to 'all'."),
|
|
51
|
+
project: projectParam,
|
|
39
52
|
}),
|
|
40
|
-
}, async ({ domain }) => {
|
|
53
|
+
}, async ({ domain, project }) => {
|
|
41
54
|
const d = domain ?? "all";
|
|
55
|
+
if (project) {
|
|
56
|
+
const err = validateProject(project);
|
|
57
|
+
if (err)
|
|
58
|
+
return mcpResponse({ ok: false, error: err });
|
|
59
|
+
const resolved = mergeConfig(phrenPath, project);
|
|
60
|
+
const projectOverrides = getProjectOverrides(phrenPath, project);
|
|
61
|
+
function src(key) {
|
|
62
|
+
return hasOwnOverride(projectOverrides, key) ? "project" : "global";
|
|
63
|
+
}
|
|
64
|
+
const result = {
|
|
65
|
+
_project: project,
|
|
66
|
+
_note: "Values marked _source=project override the global default.",
|
|
67
|
+
};
|
|
68
|
+
if (d === "all" || d === "findingSensitivity") {
|
|
69
|
+
const level = resolved.findingSensitivity;
|
|
70
|
+
result.findingSensitivity = {
|
|
71
|
+
level,
|
|
72
|
+
...FINDING_SENSITIVITY_CONFIG[level],
|
|
73
|
+
_source: src("findingSensitivity"),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (d === "all" || d === "taskMode") {
|
|
77
|
+
result.taskMode = { taskMode: resolved.taskMode, _source: src("taskMode") };
|
|
78
|
+
}
|
|
79
|
+
if (d === "all" || d === "retention") {
|
|
80
|
+
result.retention = {
|
|
81
|
+
...resolved.retentionPolicy,
|
|
82
|
+
_source: hasOwnOverride(projectOverrides, "retentionPolicy") ? "project" : "global",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (d === "all" || d === "workflow") {
|
|
86
|
+
result.workflow = {
|
|
87
|
+
...resolved.workflowPolicy,
|
|
88
|
+
_source: hasOwnOverride(projectOverrides, "workflowPolicy") ? "project" : "global",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (d === "all" || d === "proactivity") {
|
|
92
|
+
const globalSnapshot = proactivitySnapshot(phrenPath).effective;
|
|
93
|
+
const base = resolved.proactivity.base ?? globalSnapshot.proactivity;
|
|
94
|
+
const findings = resolved.proactivity.findings ?? resolved.proactivity.base ?? globalSnapshot.proactivityFindings;
|
|
95
|
+
const tasks = resolved.proactivity.tasks ?? resolved.proactivity.base ?? globalSnapshot.proactivityTask;
|
|
96
|
+
result.proactivity = {
|
|
97
|
+
base,
|
|
98
|
+
findings,
|
|
99
|
+
tasks,
|
|
100
|
+
_source: {
|
|
101
|
+
base: hasOwnOverride(projectOverrides, "proactivity") ? "project" : "global",
|
|
102
|
+
findings: hasOwnOverride(projectOverrides, "proactivityFindings")
|
|
103
|
+
? "project"
|
|
104
|
+
: hasOwnOverride(projectOverrides, "proactivity")
|
|
105
|
+
? "project"
|
|
106
|
+
: "global",
|
|
107
|
+
tasks: hasOwnOverride(projectOverrides, "proactivityTask")
|
|
108
|
+
? "project"
|
|
109
|
+
: hasOwnOverride(projectOverrides, "proactivity")
|
|
110
|
+
? "project"
|
|
111
|
+
: "global",
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (d === "all" || d === "index") {
|
|
116
|
+
result.index = getIndexPolicy(phrenPath);
|
|
117
|
+
}
|
|
118
|
+
return mcpResponse({
|
|
119
|
+
ok: true,
|
|
120
|
+
message: `Config for ${d === "all" ? "all domains" : d} (project: ${project}).`,
|
|
121
|
+
data: result,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
42
124
|
const result = {};
|
|
43
125
|
if (d === "all" || d === "proactivity") {
|
|
44
126
|
result.proactivity = proactivitySnapshot(phrenPath);
|
|
@@ -72,16 +154,36 @@ export function register(server, ctx) {
|
|
|
72
154
|
server.registerTool("set_proactivity", {
|
|
73
155
|
title: "◆ phren · set proactivity",
|
|
74
156
|
description: "Set the proactivity level for auto-capture. Controls how aggressively phren " +
|
|
75
|
-
"captures findings and tasks. Supports base level, findings-specific, and task-specific overrides."
|
|
157
|
+
"captures findings and tasks. Supports base level, findings-specific, and task-specific overrides. " +
|
|
158
|
+
"When project is provided, writes to that project's phren.project.yaml.",
|
|
76
159
|
inputSchema: z.object({
|
|
77
160
|
level: z.enum(PROACTIVITY_LEVELS).describe("Proactivity level: high, medium, or low."),
|
|
78
161
|
scope: z
|
|
79
162
|
.enum(["base", "findings", "tasks"])
|
|
80
163
|
.optional()
|
|
81
164
|
.describe("Which proactivity to set. Defaults to 'base'."),
|
|
165
|
+
project: projectParam,
|
|
82
166
|
}),
|
|
83
|
-
}, async ({ level, scope }) => {
|
|
167
|
+
}, async ({ level, scope, project }) => {
|
|
84
168
|
const s = scope ?? "base";
|
|
169
|
+
if (project) {
|
|
170
|
+
const err = validateProject(project);
|
|
171
|
+
if (err)
|
|
172
|
+
return mcpResponse({ ok: false, error: err });
|
|
173
|
+
const warning = checkProjectRegistered(phrenPath, project);
|
|
174
|
+
const key = s === "base" ? "proactivity" : s === "findings" ? "proactivityFindings" : "proactivityTask";
|
|
175
|
+
updateProjectConfigOverrides(phrenPath, project, (current) => ({
|
|
176
|
+
...current,
|
|
177
|
+
[key]: level,
|
|
178
|
+
}));
|
|
179
|
+
return mcpResponse({
|
|
180
|
+
ok: true,
|
|
181
|
+
message: warning
|
|
182
|
+
? `Proactivity ${s} set to ${level} for project "${project}". WARNING: ${warning}`
|
|
183
|
+
: `Proactivity ${s} set to ${level} for project "${project}".`,
|
|
184
|
+
data: { project, scope: s, level, ...(warning ? { warning } : {}) },
|
|
185
|
+
});
|
|
186
|
+
}
|
|
85
187
|
const patch = {};
|
|
86
188
|
if (s === "base")
|
|
87
189
|
patch.proactivity = level;
|
|
@@ -100,11 +202,30 @@ export function register(server, ctx) {
|
|
|
100
202
|
server.registerTool("set_task_mode", {
|
|
101
203
|
title: "◆ phren · set task mode",
|
|
102
204
|
description: "Set the task automation mode: off (no auto-tasks), manual (user creates), " +
|
|
103
|
-
"suggest (phren suggests, user approves), auto (phren creates automatically)."
|
|
205
|
+
"suggest (phren suggests, user approves), auto (phren creates automatically). " +
|
|
206
|
+
"When project is provided, writes to that project's phren.project.yaml.",
|
|
104
207
|
inputSchema: z.object({
|
|
105
|
-
mode: z.enum(
|
|
208
|
+
mode: z.enum(VALID_TASK_MODES).describe("Task mode: off, manual, suggest, or auto."),
|
|
209
|
+
project: projectParam,
|
|
106
210
|
}),
|
|
107
|
-
}, async ({ mode }) => {
|
|
211
|
+
}, async ({ mode, project }) => {
|
|
212
|
+
if (project) {
|
|
213
|
+
const err = validateProject(project);
|
|
214
|
+
if (err)
|
|
215
|
+
return mcpResponse({ ok: false, error: err });
|
|
216
|
+
const warning = checkProjectRegistered(phrenPath, project);
|
|
217
|
+
updateProjectConfigOverrides(phrenPath, project, (current) => ({
|
|
218
|
+
...current,
|
|
219
|
+
taskMode: mode,
|
|
220
|
+
}));
|
|
221
|
+
return mcpResponse({
|
|
222
|
+
ok: true,
|
|
223
|
+
message: warning
|
|
224
|
+
? `Task mode set to ${mode} for project "${project}". WARNING: ${warning}`
|
|
225
|
+
: `Task mode set to ${mode} for project "${project}".`,
|
|
226
|
+
data: { project, taskMode: mode, ...(warning ? { warning } : {}) },
|
|
227
|
+
});
|
|
228
|
+
}
|
|
108
229
|
const result = updateWorkflowPolicy(phrenPath, { taskMode: mode });
|
|
109
230
|
if (!result.ok) {
|
|
110
231
|
return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
|
|
@@ -120,11 +241,31 @@ export function register(server, ctx) {
|
|
|
120
241
|
title: "◆ phren · set finding sensitivity",
|
|
121
242
|
description: "Set the finding capture sensitivity level. Controls how many findings phren captures per session. " +
|
|
122
243
|
"minimal: only explicit asks. conservative: decisions/pitfalls only. " +
|
|
123
|
-
"balanced: non-obvious patterns. aggressive: capture everything."
|
|
244
|
+
"balanced: non-obvious patterns. aggressive: capture everything. " +
|
|
245
|
+
"When project is provided, writes to that project's phren.project.yaml.",
|
|
124
246
|
inputSchema: z.object({
|
|
125
|
-
level: z.enum(
|
|
247
|
+
level: z.enum(VALID_FINDING_SENSITIVITY).describe("Sensitivity level."),
|
|
248
|
+
project: projectParam,
|
|
126
249
|
}),
|
|
127
|
-
}, async ({ level }) => {
|
|
250
|
+
}, async ({ level, project }) => {
|
|
251
|
+
if (project) {
|
|
252
|
+
const err = validateProject(project);
|
|
253
|
+
if (err)
|
|
254
|
+
return mcpResponse({ ok: false, error: err });
|
|
255
|
+
const warning = checkProjectRegistered(phrenPath, project);
|
|
256
|
+
updateProjectConfigOverrides(phrenPath, project, (current) => ({
|
|
257
|
+
...current,
|
|
258
|
+
findingSensitivity: level,
|
|
259
|
+
}));
|
|
260
|
+
const config = FINDING_SENSITIVITY_CONFIG[level];
|
|
261
|
+
return mcpResponse({
|
|
262
|
+
ok: true,
|
|
263
|
+
message: warning
|
|
264
|
+
? `Finding sensitivity set to ${level} for project "${project}". WARNING: ${warning}`
|
|
265
|
+
: `Finding sensitivity set to ${level} for project "${project}".`,
|
|
266
|
+
data: { project, level, ...config, ...(warning ? { warning } : {}) },
|
|
267
|
+
});
|
|
268
|
+
}
|
|
128
269
|
const result = updateWorkflowPolicy(phrenPath, { findingSensitivity: level });
|
|
129
270
|
if (!result.ok) {
|
|
130
271
|
return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
|
|
@@ -140,7 +281,8 @@ export function register(server, ctx) {
|
|
|
140
281
|
server.registerTool("set_retention_policy", {
|
|
141
282
|
title: "◆ phren · set retention policy",
|
|
142
283
|
description: "Update memory retention policy: TTL, retention days, auto-accept threshold, " +
|
|
143
|
-
"minimum injection confidence, and decay curve."
|
|
284
|
+
"minimum injection confidence, and decay curve. " +
|
|
285
|
+
"When project is provided, writes to that project's phren.project.yaml.",
|
|
144
286
|
inputSchema: z.object({
|
|
145
287
|
ttlDays: z.number().int().min(1).optional().describe("Days before a finding is considered for expiry."),
|
|
146
288
|
retentionDays: z.number().int().min(1).optional().describe("Hard retention limit in days."),
|
|
@@ -155,20 +297,49 @@ export function register(server, ctx) {
|
|
|
155
297
|
})
|
|
156
298
|
.optional()
|
|
157
299
|
.describe("Decay multipliers at 30/60/90/120 day marks."),
|
|
300
|
+
project: projectParam,
|
|
158
301
|
}),
|
|
159
|
-
}, async ({ ttlDays, retentionDays, autoAcceptThreshold, minInjectConfidence, decay }) => {
|
|
160
|
-
|
|
302
|
+
}, async ({ ttlDays, retentionDays, autoAcceptThreshold, minInjectConfidence, decay, project }) => {
|
|
303
|
+
if (project) {
|
|
304
|
+
const err = validateProject(project);
|
|
305
|
+
if (err)
|
|
306
|
+
return mcpResponse({ ok: false, error: err });
|
|
307
|
+
const warning = checkProjectRegistered(phrenPath, project);
|
|
308
|
+
const next = updateProjectConfigOverrides(phrenPath, project, (current) => {
|
|
309
|
+
const existingRetention = current.retentionPolicy ?? {};
|
|
310
|
+
const retentionPatch = { ...existingRetention };
|
|
311
|
+
if (ttlDays !== undefined)
|
|
312
|
+
retentionPatch.ttlDays = ttlDays;
|
|
313
|
+
if (retentionDays !== undefined)
|
|
314
|
+
retentionPatch.retentionDays = retentionDays;
|
|
315
|
+
if (autoAcceptThreshold !== undefined)
|
|
316
|
+
retentionPatch.autoAcceptThreshold = autoAcceptThreshold;
|
|
317
|
+
if (minInjectConfidence !== undefined)
|
|
318
|
+
retentionPatch.minInjectConfidence = minInjectConfidence;
|
|
319
|
+
if (decay !== undefined)
|
|
320
|
+
retentionPatch.decay = { ...(existingRetention.decay ?? {}), ...decay };
|
|
321
|
+
return { ...current, retentionPolicy: retentionPatch };
|
|
322
|
+
});
|
|
323
|
+
return mcpResponse({
|
|
324
|
+
ok: true,
|
|
325
|
+
message: warning
|
|
326
|
+
? `Retention policy updated for project "${project}". WARNING: ${warning}`
|
|
327
|
+
: `Retention policy updated for project "${project}".`,
|
|
328
|
+
data: { project, retentionPolicy: next.config?.retentionPolicy ?? {}, ...(warning ? { warning } : {}) },
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
const globalPatch = {};
|
|
161
332
|
if (ttlDays !== undefined)
|
|
162
|
-
|
|
333
|
+
globalPatch.ttlDays = ttlDays;
|
|
163
334
|
if (retentionDays !== undefined)
|
|
164
|
-
|
|
335
|
+
globalPatch.retentionDays = retentionDays;
|
|
165
336
|
if (autoAcceptThreshold !== undefined)
|
|
166
|
-
|
|
337
|
+
globalPatch.autoAcceptThreshold = autoAcceptThreshold;
|
|
167
338
|
if (minInjectConfidence !== undefined)
|
|
168
|
-
|
|
339
|
+
globalPatch.minInjectConfidence = minInjectConfidence;
|
|
169
340
|
if (decay !== undefined)
|
|
170
|
-
|
|
171
|
-
const result = updateRetentionPolicy(phrenPath,
|
|
341
|
+
globalPatch.decay = decay;
|
|
342
|
+
const result = updateRetentionPolicy(phrenPath, globalPatch);
|
|
172
343
|
if (!result.ok) {
|
|
173
344
|
return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
|
|
174
345
|
}
|
|
@@ -182,18 +353,52 @@ export function register(server, ctx) {
|
|
|
182
353
|
server.registerTool("set_workflow_policy", {
|
|
183
354
|
title: "◆ phren · set workflow policy",
|
|
184
355
|
description: "Update workflow policy: low-confidence threshold, " +
|
|
185
|
-
"risky sections list, task mode, and finding sensitivity."
|
|
356
|
+
"risky sections list, task mode, and finding sensitivity. " +
|
|
357
|
+
"When project is provided, writes to that project's phren.project.yaml.",
|
|
186
358
|
inputSchema: z.object({
|
|
187
359
|
lowConfidenceThreshold: z.number().min(0).max(1).optional()
|
|
188
360
|
.describe("Confidence below which items are flagged as low-confidence."),
|
|
189
361
|
riskySections: z.array(z.enum(["Review", "Stale", "Conflicts"])).optional()
|
|
190
362
|
.describe("Which queue sections are considered risky."),
|
|
191
|
-
taskMode: z.enum(
|
|
363
|
+
taskMode: z.enum(VALID_TASK_MODES).optional()
|
|
192
364
|
.describe("Task automation mode."),
|
|
193
|
-
findingSensitivity: z.enum(
|
|
365
|
+
findingSensitivity: z.enum(VALID_FINDING_SENSITIVITY).optional()
|
|
194
366
|
.describe("Finding capture sensitivity."),
|
|
367
|
+
project: projectParam,
|
|
195
368
|
}),
|
|
196
|
-
}, async ({ lowConfidenceThreshold, riskySections, taskMode, findingSensitivity }) => {
|
|
369
|
+
}, async ({ lowConfidenceThreshold, riskySections, taskMode, findingSensitivity, project }) => {
|
|
370
|
+
if (project) {
|
|
371
|
+
const err = validateProject(project);
|
|
372
|
+
if (err)
|
|
373
|
+
return mcpResponse({ ok: false, error: err });
|
|
374
|
+
const warning = checkProjectRegistered(phrenPath, project);
|
|
375
|
+
const next = updateProjectConfigOverrides(phrenPath, project, (current) => {
|
|
376
|
+
const nextConfig = { ...current };
|
|
377
|
+
const shouldUpdateWorkflowPolicy = (lowConfidenceThreshold !== undefined
|
|
378
|
+
|| riskySections !== undefined
|
|
379
|
+
|| current.workflowPolicy !== undefined);
|
|
380
|
+
if (shouldUpdateWorkflowPolicy) {
|
|
381
|
+
const existingWorkflow = current.workflowPolicy ?? {};
|
|
382
|
+
nextConfig.workflowPolicy = {
|
|
383
|
+
...existingWorkflow,
|
|
384
|
+
...(lowConfidenceThreshold !== undefined ? { lowConfidenceThreshold } : {}),
|
|
385
|
+
...(riskySections !== undefined ? { riskySections } : {}),
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
if (taskMode !== undefined)
|
|
389
|
+
nextConfig.taskMode = taskMode;
|
|
390
|
+
if (findingSensitivity !== undefined)
|
|
391
|
+
nextConfig.findingSensitivity = findingSensitivity;
|
|
392
|
+
return nextConfig;
|
|
393
|
+
});
|
|
394
|
+
return mcpResponse({
|
|
395
|
+
ok: true,
|
|
396
|
+
message: warning
|
|
397
|
+
? `Workflow policy updated for project "${project}". WARNING: ${warning}`
|
|
398
|
+
: `Workflow policy updated for project "${project}".`,
|
|
399
|
+
data: { project, config: next.config ?? {}, ...(warning ? { warning } : {}) },
|
|
400
|
+
});
|
|
401
|
+
}
|
|
197
402
|
const patch = {};
|
|
198
403
|
if (lowConfidenceThreshold !== undefined)
|
|
199
404
|
patch.lowConfidenceThreshold = lowConfidenceThreshold;
|
package/mcp/dist/mcp-data.js
CHANGED
|
@@ -2,7 +2,7 @@ import { mcpResponse } from "./mcp-types.js";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import * as fs from "fs";
|
|
4
4
|
import * as path from "path";
|
|
5
|
-
import { isValidProjectName } from "./utils.js";
|
|
5
|
+
import { isValidProjectName, errorMessage } from "./utils.js";
|
|
6
6
|
import { readFindings, readTasks, resolveTaskFilePath, TASKS_FILENAME } from "./data-access.js";
|
|
7
7
|
import { debugLog, findProjectNameCaseInsensitive, normalizeProjectNameForCreate } from "./shared.js";
|
|
8
8
|
const importPayloadSchema = z.object({
|
|
@@ -75,8 +75,8 @@ export function register(server, ctx) {
|
|
|
75
75
|
decoded = JSON.parse(rawData);
|
|
76
76
|
}
|
|
77
77
|
catch (err) {
|
|
78
|
-
if ((process.env.PHREN_DEBUG
|
|
79
|
-
process.stderr.write(`[phren] import_project jsonParse: ${
|
|
78
|
+
if ((process.env.PHREN_DEBUG))
|
|
79
|
+
process.stderr.write(`[phren] import_project jsonParse: ${errorMessage(err)}\n`);
|
|
80
80
|
return mcpResponse({ ok: false, error: "Invalid JSON input." });
|
|
81
81
|
}
|
|
82
82
|
const parsedResult = importPayloadSchema.safeParse(decoded);
|
|
@@ -241,8 +241,8 @@ export function register(server, ctx) {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
catch (err) {
|
|
244
|
-
if ((process.env.PHREN_DEBUG
|
|
245
|
-
process.stderr.write(`[phren] import_project backupRestore: ${
|
|
244
|
+
if ((process.env.PHREN_DEBUG))
|
|
245
|
+
process.stderr.write(`[phren] import_project backupRestore: ${errorMessage(err)}\n`);
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
return mcpResponse({
|
|
@@ -261,8 +261,8 @@ export function register(server, ctx) {
|
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
catch (err) {
|
|
264
|
-
if ((process.env.PHREN_DEBUG
|
|
265
|
-
process.stderr.write(`[phren] import_project backupCleanup: ${
|
|
264
|
+
if ((process.env.PHREN_DEBUG))
|
|
265
|
+
process.stderr.write(`[phren] import_project backupCleanup: ${errorMessage(err)}\n`);
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
268
|
return mcpResponse({
|
|
@@ -299,7 +299,7 @@ export function register(server, ctx) {
|
|
|
299
299
|
}
|
|
300
300
|
catch (err) {
|
|
301
301
|
fs.renameSync(archiveDir, projectDir);
|
|
302
|
-
return mcpResponse({ ok: false, error: `Index rebuild failed after archive rename, rolled back: ${
|
|
302
|
+
return mcpResponse({ ok: false, error: `Index rebuild failed after archive rename, rolled back: ${errorMessage(err)}` });
|
|
303
303
|
}
|
|
304
304
|
return mcpResponse({
|
|
305
305
|
ok: true,
|
|
@@ -322,7 +322,7 @@ export function register(server, ctx) {
|
|
|
322
322
|
}
|
|
323
323
|
catch (err) {
|
|
324
324
|
fs.renameSync(projectDir, archiveDir);
|
|
325
|
-
return mcpResponse({ ok: false, error: `Index rebuild failed after unarchive rename, rolled back: ${
|
|
325
|
+
return mcpResponse({ ok: false, error: `Index rebuild failed after unarchive rename, rolled back: ${errorMessage(err)}` });
|
|
326
326
|
}
|
|
327
327
|
return mcpResponse({
|
|
328
328
|
ok: true,
|
|
@@ -24,7 +24,7 @@ export function readExtractedFacts(phrenPath, project) {
|
|
|
24
24
|
return Array.isArray(data) ? data : [];
|
|
25
25
|
}
|
|
26
26
|
catch (err) {
|
|
27
|
-
if ((process.env.PHREN_DEBUG
|
|
27
|
+
if ((process.env.PHREN_DEBUG))
|
|
28
28
|
process.stderr.write(`[phren] readExtractedFacts: ${errorMessage(err)}\n`);
|
|
29
29
|
return [];
|
|
30
30
|
}
|
package/mcp/dist/mcp-extract.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mcpResponse } from "./mcp-types.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { isValidProjectName, safeProjectPath } from "./utils.js";
|
|
3
|
+
import { isValidProjectName, safeProjectPath, errorMessage } from "./utils.js";
|
|
4
4
|
import { addFindingsToFile } from "./shared-content.js";
|
|
5
5
|
import { checkOllamaAvailable, checkModelAvailable, generateText, getOllamaUrl, getExtractModel } from "./shared-ollama.js";
|
|
6
6
|
import { debugLog } from "./shared.js";
|
|
@@ -34,7 +34,7 @@ function parseFindings(raw) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
catch (err) {
|
|
37
|
-
debugLog(`auto_extract: failed to parse LLM output as JSON: ${cleaned.slice(0, 200)} (${
|
|
37
|
+
debugLog(`auto_extract: failed to parse LLM output as JSON: ${cleaned.slice(0, 200)} (${errorMessage(err)})`);
|
|
38
38
|
}
|
|
39
39
|
return [];
|
|
40
40
|
}
|
package/mcp/dist/mcp-finding.js
CHANGED
|
@@ -236,7 +236,7 @@ export function register(server, ctx) {
|
|
|
236
236
|
extraAnnotationsByFinding.push(conflicts.checked && conflicts.annotations.length > 0 ? conflicts.annotations : []);
|
|
237
237
|
}
|
|
238
238
|
catch (err) {
|
|
239
|
-
if ((process.env.PHREN_DEBUG
|
|
239
|
+
if ((process.env.PHREN_DEBUG))
|
|
240
240
|
process.stderr.write(`[phren] add_findings semanticConflict: ${errorMessage(err)}\n`);
|
|
241
241
|
extraAnnotationsByFinding.push([]);
|
|
242
242
|
}
|
|
@@ -499,7 +499,7 @@ export function register(server, ctx) {
|
|
|
499
499
|
hasRemote = remotes.length > 0;
|
|
500
500
|
}
|
|
501
501
|
catch (err) {
|
|
502
|
-
if ((process.env.PHREN_DEBUG
|
|
502
|
+
if ((process.env.PHREN_DEBUG))
|
|
503
503
|
process.stderr.write(`[phren] push_changes remoteCheck: ${errorMessage(err)}\n`);
|
|
504
504
|
}
|
|
505
505
|
if (!hasRemote) {
|
|
@@ -523,7 +523,7 @@ export function register(server, ctx) {
|
|
|
523
523
|
runGit(["pull", "--rebase", "--quiet"], { timeout: 15000 });
|
|
524
524
|
}
|
|
525
525
|
catch (pullErr) {
|
|
526
|
-
if ((process.env.PHREN_DEBUG
|
|
526
|
+
if ((process.env.PHREN_DEBUG))
|
|
527
527
|
process.stderr.write(`[phren] push_changes pullRebase: ${pullErr instanceof Error ? pullErr.message : String(pullErr)}\n`);
|
|
528
528
|
const resolved = autoMergeConflicts(phrenPath);
|
|
529
529
|
if (resolved) {
|
|
@@ -534,13 +534,13 @@ export function register(server, ctx) {
|
|
|
534
534
|
});
|
|
535
535
|
}
|
|
536
536
|
catch (continueErr) {
|
|
537
|
-
if ((process.env.PHREN_DEBUG
|
|
537
|
+
if ((process.env.PHREN_DEBUG))
|
|
538
538
|
process.stderr.write(`[phren] push_changes rebaseContinue: ${continueErr instanceof Error ? continueErr.message : String(continueErr)}\n`);
|
|
539
539
|
try {
|
|
540
540
|
runGit(["rebase", "--abort"]);
|
|
541
541
|
}
|
|
542
542
|
catch (abortErr) {
|
|
543
|
-
if ((process.env.PHREN_DEBUG
|
|
543
|
+
if ((process.env.PHREN_DEBUG))
|
|
544
544
|
process.stderr.write(`[phren] push_changes rebaseAbort: ${abortErr instanceof Error ? abortErr.message : String(abortErr)}\n`);
|
|
545
545
|
}
|
|
546
546
|
break;
|
|
@@ -551,7 +551,7 @@ export function register(server, ctx) {
|
|
|
551
551
|
runGit(["rebase", "--abort"]);
|
|
552
552
|
}
|
|
553
553
|
catch (abortErr) {
|
|
554
|
-
if ((process.env.PHREN_DEBUG
|
|
554
|
+
if ((process.env.PHREN_DEBUG))
|
|
555
555
|
process.stderr.write(`[phren] push_changes rebaseAbort2: ${abortErr instanceof Error ? abortErr.message : String(abortErr)}\n`);
|
|
556
556
|
}
|
|
557
557
|
break;
|
package/mcp/dist/mcp-graph.js
CHANGED
|
@@ -2,7 +2,7 @@ import { mcpResponse } from "./mcp-types.js";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import * as fs from "fs";
|
|
4
4
|
import * as crypto from "crypto";
|
|
5
|
-
import { isValidProjectName } from "./utils.js";
|
|
5
|
+
import { isValidProjectName, errorMessage } from "./utils.js";
|
|
6
6
|
import { queryDocBySourceKey, queryRows, queryFragmentLinks, queryCrossProjectFragments, ensureGlobalEntitiesTable, logFragmentMiss } from "./shared-index.js";
|
|
7
7
|
import { runtimeFile } from "./shared.js";
|
|
8
8
|
import { withFileLock } from "./shared-governance.js";
|
|
@@ -209,8 +209,8 @@ export function register(server, ctx) {
|
|
|
209
209
|
db.run("INSERT OR IGNORE INTO entities (name, type, first_seen_at) VALUES (?, ?, ?)", [fragmentName, resolvedFragmentType, new Date().toISOString().slice(0, 10)]);
|
|
210
210
|
}
|
|
211
211
|
catch (err) {
|
|
212
|
-
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG
|
|
213
|
-
process.stderr.write(`[phren] link_findings fragmentInsert: ${
|
|
212
|
+
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG))
|
|
213
|
+
process.stderr.write(`[phren] link_findings fragmentInsert: ${errorMessage(err)}\n`);
|
|
214
214
|
}
|
|
215
215
|
const fragmentResult = db.exec("SELECT id FROM entities WHERE name = ? AND type = ?", [fragmentName, resolvedFragmentType]);
|
|
216
216
|
if (!fragmentResult?.length || !fragmentResult[0]?.values?.length) {
|
|
@@ -232,8 +232,8 @@ export function register(server, ctx) {
|
|
|
232
232
|
db.run("INSERT OR IGNORE INTO entities (name, type, first_seen_at) VALUES (?, ?, ?)", [sourceDoc, "document", new Date().toISOString().slice(0, 10)]);
|
|
233
233
|
}
|
|
234
234
|
catch (err) {
|
|
235
|
-
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG
|
|
236
|
-
process.stderr.write(`[phren] link_findings docFragmentInsert: ${
|
|
235
|
+
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG))
|
|
236
|
+
process.stderr.write(`[phren] link_findings docFragmentInsert: ${errorMessage(err)}\n`);
|
|
237
237
|
}
|
|
238
238
|
const docFragmentResult = db.exec("SELECT id FROM entities WHERE name = ? AND type = ?", [sourceDoc, "document"]);
|
|
239
239
|
if (!docFragmentResult?.length || !docFragmentResult[0]?.values?.length) {
|
|
@@ -245,8 +245,8 @@ export function register(server, ctx) {
|
|
|
245
245
|
db.run("INSERT OR IGNORE INTO entity_links (source_id, target_id, rel_type, source_doc) VALUES (?, ?, ?, ?)", [sourceId, targetId, relType, sourceDoc]);
|
|
246
246
|
}
|
|
247
247
|
catch (err) {
|
|
248
|
-
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG
|
|
249
|
-
process.stderr.write(`[phren] link_findings linkInsert: ${
|
|
248
|
+
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG))
|
|
249
|
+
process.stderr.write(`[phren] link_findings linkInsert: ${errorMessage(err)}\n`);
|
|
250
250
|
return mcpResponse({ ok: false, error: "Failed to insert fragment link." });
|
|
251
251
|
}
|
|
252
252
|
// 4a. Also populate global_entities so manual links appear in cross_project_fragments
|
|
@@ -255,8 +255,8 @@ export function register(server, ctx) {
|
|
|
255
255
|
db.run("INSERT OR IGNORE INTO global_entities (entity, project, doc_key) VALUES (?, ?, ?)", [fragmentName, project, sourceDoc]);
|
|
256
256
|
}
|
|
257
257
|
catch (err) {
|
|
258
|
-
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG
|
|
259
|
-
process.stderr.write(`[phren] link_findings globalFragments: ${
|
|
258
|
+
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG))
|
|
259
|
+
process.stderr.write(`[phren] link_findings globalFragments: ${errorMessage(err)}\n`);
|
|
260
260
|
}
|
|
261
261
|
// 4b. Persist manual link so it survives index rebuilds (mandatory — failure aborts the operation)
|
|
262
262
|
const manualLinksPath = runtimeFile(ctx.phrenPath, "manual-links.json");
|
|
@@ -268,8 +268,8 @@ export function register(server, ctx) {
|
|
|
268
268
|
existing = JSON.parse(fs.readFileSync(manualLinksPath, "utf8"));
|
|
269
269
|
}
|
|
270
270
|
catch (err) {
|
|
271
|
-
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG
|
|
272
|
-
process.stderr.write(`[phren] link_findings manualLinksRead: ${
|
|
271
|
+
if (process.env.PHREN_DEBUG || (process.env.PHREN_DEBUG))
|
|
272
|
+
process.stderr.write(`[phren] link_findings manualLinksRead: ${errorMessage(err)}\n`);
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
const newEntry = { entity: fragmentName, entityType: resolvedFragmentType, sourceDoc, relType };
|