@phren/cli 0.0.27 → 0.0.32
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/mcp/dist/capabilities/cli.js +2 -5
- package/mcp/dist/capabilities/mcp.js +5 -8
- package/mcp/dist/capabilities/types.js +2 -5
- package/mcp/dist/capabilities/vscode.js +2 -5
- package/mcp/dist/capabilities/web-ui.js +2 -5
- package/mcp/dist/{cli-actions.js → cli/actions.js} +22 -21
- package/mcp/dist/{cli.js → cli/cli.js} +13 -13
- package/mcp/dist/{cli-config.js → cli/config.js} +9 -9
- package/mcp/dist/{cli-extract.js → cli/extract.js} +8 -8
- package/mcp/dist/{cli-govern.js → cli/govern.js} +10 -9
- package/mcp/dist/{cli-graph.js → cli/graph.js} +10 -9
- package/mcp/dist/{cli-hooks-citations.js → cli/hooks-citations.js} +2 -2
- package/mcp/dist/{cli-hooks-context.js → cli/hooks-context.js} +23 -23
- package/mcp/dist/{cli-hooks-globs.js → cli/hooks-globs.js} +4 -4
- package/mcp/dist/{cli-hooks-output.js → cli/hooks-output.js} +9 -10
- package/mcp/dist/{cli-hooks-session.js → cli/hooks-session.js} +42 -57
- package/mcp/dist/{cli-hooks.js → cli/hooks.js} +27 -26
- package/mcp/dist/{cli-namespaces.js → cli/namespaces.js} +25 -24
- package/mcp/dist/{cli-ops.js → cli/ops.js} +9 -9
- package/mcp/dist/{cli-search.js → cli/search.js} +8 -7
- package/mcp/dist/cli-hooks-git.js +243 -0
- package/mcp/dist/cli-hooks-prompt.js +319 -0
- package/mcp/dist/cli-hooks-session-handlers.js +349 -0
- package/mcp/dist/cli-hooks-stop.js +557 -0
- package/mcp/dist/{content-archive.js → content/archive.js} +8 -9
- package/mcp/dist/{content-citation.js → content/citation.js} +5 -5
- package/mcp/dist/{content-dedup.js → content/dedup.js} +9 -12
- package/mcp/dist/{content-learning.js → content/learning.js} +12 -12
- package/mcp/dist/{content-validate.js → content/validate.js} +5 -5
- package/mcp/dist/{core-finding.js → core/finding.js} +4 -4
- package/mcp/dist/{core-project.js → core/project.js} +4 -4
- package/mcp/dist/{core-search.js → core/search.js} +2 -2
- package/mcp/dist/{data-access.js → data/access.js} +131 -13
- package/mcp/dist/{data-tasks.js → data/tasks.js} +7 -5
- package/mcp/dist/embedding.js +9 -14
- package/mcp/dist/entrypoint.js +13 -11
- package/mcp/dist/{finding-context.js → finding/context.js} +2 -2
- package/mcp/dist/{finding-impact.js → finding/impact.js} +3 -3
- package/mcp/dist/{finding-journal.js → finding/journal.js} +4 -4
- package/mcp/dist/{finding-lifecycle.js → finding/lifecycle.js} +4 -4
- package/mcp/dist/{governance-audit.js → governance/audit.js} +2 -2
- package/mcp/dist/{governance-locks.js → governance/locks.js} +14 -9
- package/mcp/dist/{governance-policy.js → governance/policy.js} +10 -12
- package/mcp/dist/{governance-rbac.js → governance/rbac.js} +3 -3
- package/mcp/dist/{governance-scores.js → governance/scores.js} +8 -10
- package/mcp/dist/hooks.js +39 -31
- package/mcp/dist/index-query.js +4 -1
- package/mcp/dist/index.js +53 -29
- package/mcp/dist/{init-config.js → init/config.js} +6 -6
- package/mcp/dist/{init.js → init/init.js} +28 -29
- package/mcp/dist/{init-preferences.js → init/preferences.js} +3 -3
- package/mcp/dist/{init-setup.js → init/setup.js} +17 -19
- package/mcp/dist/{init-shared.js → init/shared.js} +3 -3
- package/mcp/dist/init-bootstrap.js +68 -0
- package/mcp/dist/init-detect.js +38 -0
- package/mcp/dist/init-dryrun.js +55 -0
- package/mcp/dist/init-env.js +114 -0
- package/mcp/dist/init-fresh.js +239 -0
- package/mcp/dist/init-hooks.js +26 -0
- package/mcp/dist/init-mcp.js +65 -0
- package/mcp/dist/init-migrate.js +51 -0
- package/mcp/dist/init-modes.js +135 -0
- package/mcp/dist/init-npm.js +37 -0
- package/mcp/dist/init-project-local.js +99 -0
- package/mcp/dist/init-semantic.js +48 -0
- package/mcp/dist/init-types.js +1 -0
- package/mcp/dist/init-uninstall.js +482 -0
- package/mcp/dist/init-update.js +96 -0
- package/mcp/dist/init-walkthrough-merge.js +90 -0
- package/mcp/dist/init-walkthrough.js +529 -0
- package/mcp/dist/{link-checksums.js → link/checksums.js} +5 -5
- package/mcp/dist/{link-context.js → link/context.js} +4 -4
- package/mcp/dist/{link-doctor.js → link/doctor.js} +20 -22
- package/mcp/dist/{link.js → link/link.js} +26 -31
- package/mcp/dist/{link-skills.js → link/skills.js} +10 -10
- package/mcp/dist/logger.js +11 -3
- package/mcp/dist/phren-art.js +0 -6
- package/mcp/dist/phren-paths.js +30 -12
- package/mcp/dist/proactivity.js +2 -2
- package/mcp/dist/profile-store.js +5 -6
- package/mcp/dist/project-config.js +2 -2
- package/mcp/dist/project-topics.js +1 -1
- package/mcp/dist/query-correlation.js +1 -1
- package/mcp/dist/{session-checkpoints.js → session/checkpoints.js} +3 -3
- package/mcp/dist/{session-utils.js → session/utils.js} +1 -1
- package/mcp/dist/{shared-content.js → shared/content.js} +7 -7
- package/mcp/dist/{shared-data-utils.js → shared/data-utils.js} +3 -3
- package/mcp/dist/{shared-embedding-cache.js → shared/embedding-cache.js} +3 -3
- package/mcp/dist/{shared-fragment-graph.js → shared/fragment-graph.js} +15 -24
- package/mcp/dist/shared/governance.js +4 -0
- package/mcp/dist/{shared-index.js → shared/index.js} +92 -123
- package/mcp/dist/{shared-ollama.js → shared/ollama.js} +2 -2
- package/mcp/dist/{shared-retrieval.js → shared/retrieval.js} +16 -21
- package/mcp/dist/{shared-search-fallback.js → shared/search-fallback.js} +17 -20
- package/mcp/dist/{shared-sqljs.js → shared/sqljs.js} +3 -3
- package/mcp/dist/{shared-vector-index.js → shared/vector-index.js} +3 -3
- package/mcp/dist/shared.js +4 -59
- package/mcp/dist/{shell-entry.js → shell/entry.js} +6 -6
- package/mcp/dist/{shell-input.js → shell/input.js} +13 -13
- package/mcp/dist/{shell-palette.js → shell/palette.js} +3 -3
- package/mcp/dist/{shell-render.js → shell/render.js} +1 -1
- package/mcp/dist/{shell.js → shell/shell.js} +11 -11
- package/mcp/dist/{shell-state-store.js → shell/state-store.js} +5 -5
- package/mcp/dist/{shell-view-list.js → shell/view-list.js} +1 -1
- package/mcp/dist/{shell-view.js → shell/view.js} +13 -13
- package/mcp/dist/{skill-files.js → skill/files.js} +9 -9
- package/mcp/dist/{skill-registry.js → skill/registry.js} +4 -4
- package/mcp/dist/{skill-state.js → skill/state.js} +1 -1
- package/mcp/dist/startup-embedding.js +2 -2
- package/mcp/dist/status.js +15 -14
- package/mcp/dist/{tasks-github.js → task/github.js} +2 -2
- package/mcp/dist/{task-hygiene.js → task/hygiene.js} +4 -4
- package/mcp/dist/{task-lifecycle.js → task/lifecycle.js} +7 -7
- package/mcp/dist/telemetry.js +3 -4
- package/mcp/dist/tool-registry.js +29 -17
- package/mcp/dist/tools/config.js +515 -0
- package/mcp/dist/{mcp-data.js → tools/data.js} +8 -10
- package/mcp/dist/{mcp-extract-facts.js → tools/extract-facts.js} +6 -6
- package/mcp/dist/{mcp-extract.js → tools/extract.js} +6 -6
- package/mcp/dist/{mcp-finding.js → tools/finding.js} +97 -124
- package/mcp/dist/{mcp-graph.js → tools/graph.js} +11 -14
- package/mcp/dist/{mcp-hooks.js → tools/hooks.js} +6 -6
- package/mcp/dist/{mcp-memory.js → tools/memory.js} +5 -5
- package/mcp/dist/{mcp-ops.js → tools/ops.js} +169 -71
- package/mcp/dist/{mcp-search.js → tools/search.js} +19 -23
- package/mcp/dist/{mcp-session.js → tools/session.js} +48 -23
- package/mcp/dist/{mcp-skills.js → tools/skills.js} +33 -35
- package/mcp/dist/{mcp-tasks.js → tools/tasks.js} +155 -282
- package/mcp/dist/{memory-ui-data.js → ui/data.js} +31 -17
- package/mcp/dist/{memory-ui.js → ui/memory-ui.js} +3 -3
- package/mcp/dist/{memory-ui-page.js → ui/page.js} +4 -6
- package/mcp/dist/{memory-ui-server.js → ui/server.js} +30 -22
- package/mcp/dist/update.js +2 -2
- package/mcp/dist/utils.js +51 -11
- package/package.json +17 -11
- package/scripts/preuninstall.mjs +139 -0
- package/starter/global/CLAUDE.md +3 -2
- package/mcp/dist/mcp-config.js +0 -551
- package/mcp/dist/shared-governance.js +0 -4
- package/starter/global/skills/pipeline.md +0 -35
- package/starter/global/skills/release.md +0 -35
- /package/mcp/dist/{content-metadata.js → content/metadata.js} +0 -0
- /package/mcp/dist/{shared-stemmer.js → shared/stemmer.js} +0 -0
- /package/mcp/dist/{shell-types.js → shell/types.js} +0 -0
- /package/mcp/dist/{mcp-types.js → tools/types.js} +0 -0
- /package/mcp/dist/{memory-ui-assets.js → ui/assets.js} +0 -0
- /package/mcp/dist/{memory-ui-graph.js → ui/graph.js} +0 -0
- /package/mcp/dist/{memory-ui-scripts.js → ui/scripts.js} +0 -0
- /package/mcp/dist/{memory-ui-styles.js → ui/styles.js} +0 -0
package/mcp/dist/telemetry.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { runtimeDir } from "./shared.js";
|
|
4
4
|
import { errorMessage } from "./utils.js";
|
|
5
|
+
import { logger } from "./logger.js";
|
|
5
6
|
// In-memory buffers keyed by phrenPath to batch disk writes
|
|
6
7
|
// Keeping per-path buffers avoids silently losing events when the active path changes.
|
|
7
8
|
const buffers = new Map();
|
|
@@ -26,8 +27,7 @@ function loadFromDisk(phrenPath) {
|
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
catch (err) {
|
|
29
|
-
|
|
30
|
-
process.stderr.write(`[phren] telemetry loadFromDisk: ${errorMessage(err)}\n`);
|
|
30
|
+
logger.debug("telemetry loadFromDisk", errorMessage(err));
|
|
31
31
|
return defaults;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -58,8 +58,7 @@ function flushTelemetryForPath(phrenPath) {
|
|
|
58
58
|
fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
|
|
59
59
|
}
|
|
60
60
|
catch (err) {
|
|
61
|
-
|
|
62
|
-
process.stderr.write(`[phren] telemetry flush: ${errorMessage(err)}\n`);
|
|
61
|
+
logger.debug("telemetry flush", errorMessage(err));
|
|
63
62
|
}
|
|
64
63
|
pendingCounts.set(phrenPath, 0);
|
|
65
64
|
}
|
|
@@ -2,18 +2,18 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
const CATEGORY_BY_MODULE = {
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
5
|
+
"search": "Search and browse",
|
|
6
|
+
"tasks": "Task management",
|
|
7
|
+
"finding": "Finding capture",
|
|
8
|
+
"memory": "Memory quality",
|
|
9
|
+
"data": "Data management",
|
|
10
|
+
"graph": "Fragments and graph",
|
|
11
|
+
"session": "Session management",
|
|
12
|
+
"ops": "Operations and review",
|
|
13
|
+
"skills": "Skills management",
|
|
14
|
+
"hooks": "Hooks management",
|
|
15
|
+
"config": "Configuration",
|
|
16
|
+
"extract": "Extraction",
|
|
17
17
|
};
|
|
18
18
|
const MODULE_ORDER = Object.keys(CATEGORY_BY_MODULE);
|
|
19
19
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -45,7 +45,7 @@ function parseModuleTools(moduleName, source) {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
// Handle loop-registered tools: { tool: "name", ... } patterns followed by registerTool(action.tool, ...)
|
|
48
|
-
const loopPattern = /for\s*\(const\s+(\w+)\s+of\s+\[([\s\S]*?)\]\s*(?:as\s+const\s*)?\)\s*\{\s
|
|
48
|
+
const loopPattern = /for\s*\(const\s+(\w+)\s+of\s+\[([\s\S]*?)\]\s*(?:as\s+const\s*)?\)\s*\{[\s\S]*?server\.registerTool\(\s*\1\.tool\s*,\s*\{([\s\S]*?)\}\s*,/g;
|
|
49
49
|
for (const loopMatch of source.matchAll(loopPattern)) {
|
|
50
50
|
const [, , itemsBlock, configBlock] = loopMatch;
|
|
51
51
|
const itemPattern = /\{\s*tool:\s*"([^"]+)"[^}]*verb:\s*"([^"]+)"/g;
|
|
@@ -66,11 +66,23 @@ function parseModuleTools(moduleName, source) {
|
|
|
66
66
|
export function getRegisteredTools() {
|
|
67
67
|
const dir = sourceDir();
|
|
68
68
|
const entries = [];
|
|
69
|
+
const toolsDir = path.join(dir, "tools");
|
|
69
70
|
for (const moduleName of MODULE_ORDER) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
71
|
+
// Tool files may live in the tools/ subdirectory or alongside tool-registry.ts
|
|
72
|
+
let sourcePath;
|
|
73
|
+
for (const base of [toolsDir, dir]) {
|
|
74
|
+
const tsPath = path.join(base, `${moduleName}.ts`);
|
|
75
|
+
const jsPath = path.join(base, `${moduleName}.js`);
|
|
76
|
+
if (fs.existsSync(tsPath)) {
|
|
77
|
+
sourcePath = tsPath;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
if (fs.existsSync(jsPath)) {
|
|
81
|
+
sourcePath = jsPath;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!sourcePath)
|
|
74
86
|
continue;
|
|
75
87
|
entries.push(...parseModuleTools(moduleName, fs.readFileSync(sourcePath, "utf8")));
|
|
76
88
|
}
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { mcpResponse } from "./types.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { getRetentionPolicy, updateRetentionPolicy, getWorkflowPolicy, updateWorkflowPolicy, getIndexPolicy, updateIndexPolicy, mergeConfig, VALID_TASK_MODES, VALID_FINDING_SENSITIVITY, } from "../shared/governance.js";
|
|
6
|
+
import { PROACTIVITY_LEVELS, } from "../proactivity.js";
|
|
7
|
+
import { writeGovernanceInstallPreferences, } from "../init/preferences.js";
|
|
8
|
+
import { FINDING_SENSITIVITY_CONFIG, buildProactivitySnapshot, checkProjectInProfile } from "../cli/config.js";
|
|
9
|
+
import { readProjectConfig, updateProjectConfigOverrides, } from "../project-config.js";
|
|
10
|
+
import { isValidProjectName, safeProjectPath } from "../utils.js";
|
|
11
|
+
import { readProjectTopics, writeProjectTopics, } from "../project-topics.js";
|
|
12
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
13
|
+
function proactivitySnapshot(phrenPath) {
|
|
14
|
+
const snap = buildProactivitySnapshot(phrenPath);
|
|
15
|
+
return { configured: snap.configured, effective: snap.effective };
|
|
16
|
+
}
|
|
17
|
+
function validateProject(project) {
|
|
18
|
+
if (!isValidProjectName(project))
|
|
19
|
+
return `Invalid project name: "${project}".`;
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function checkProjectRegistered(phrenPath, project) {
|
|
23
|
+
const warning = checkProjectInProfile(phrenPath, project);
|
|
24
|
+
if (warning) {
|
|
25
|
+
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.`;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function normalizeProjectOverrides(raw) {
|
|
30
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
31
|
+
}
|
|
32
|
+
function getProjectOverrides(phrenPath, project) {
|
|
33
|
+
return normalizeProjectOverrides(readProjectConfig(phrenPath, project).config);
|
|
34
|
+
}
|
|
35
|
+
function hasOwnOverride(overrides, key) {
|
|
36
|
+
return Object.prototype.hasOwnProperty.call(overrides, key);
|
|
37
|
+
}
|
|
38
|
+
const projectParam = z.string().optional().describe("Project name. When provided, writes to that project's phren.project.yaml instead of global .config/.");
|
|
39
|
+
// ── Topic helpers (shared by get_config topic domain and set_config topic domain) ──
|
|
40
|
+
function getTopicConfigData(phrenPath, project) {
|
|
41
|
+
const projectDir = safeProjectPath(phrenPath, project);
|
|
42
|
+
if (!projectDir || !fs.existsSync(projectDir)) {
|
|
43
|
+
return { ok: false, error: `Project "${project}" not found in phren.` };
|
|
44
|
+
}
|
|
45
|
+
const result = readProjectTopics(phrenPath, project);
|
|
46
|
+
const configPath = path.join(projectDir, "topic-config.json");
|
|
47
|
+
const raw = fs.existsSync(configPath)
|
|
48
|
+
? (() => { try {
|
|
49
|
+
return JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
} })()
|
|
54
|
+
: null;
|
|
55
|
+
return {
|
|
56
|
+
ok: true,
|
|
57
|
+
data: {
|
|
58
|
+
project,
|
|
59
|
+
source: result.source,
|
|
60
|
+
domain: result.domain ?? raw?.domain ?? null,
|
|
61
|
+
topics: result.topics,
|
|
62
|
+
pinnedTopics: raw?.pinnedTopics ?? [],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// ── Registration ────────────────────────────────────────────────────────────
|
|
67
|
+
export function register(server, ctx) {
|
|
68
|
+
const { phrenPath } = ctx;
|
|
69
|
+
// ── get_config ────────────────────────────────────────────────────────────
|
|
70
|
+
server.registerTool("get_config", {
|
|
71
|
+
title: "◆ phren · get config",
|
|
72
|
+
description: "Read current configuration for one or all config domains: proactivity, taskMode, " +
|
|
73
|
+
"findingSensitivity, retention (policy), workflow, access, index, topic. " +
|
|
74
|
+
"Returns both configured and effective values. When project is provided, returns " +
|
|
75
|
+
"the merged view with project overrides applied and _source annotations.",
|
|
76
|
+
inputSchema: z.object({
|
|
77
|
+
domain: z
|
|
78
|
+
.enum(["proactivity", "taskMode", "findingSensitivity", "retention", "workflow", "access", "index", "topic", "all"])
|
|
79
|
+
.optional()
|
|
80
|
+
.describe("Config domain to read. Defaults to 'all'."),
|
|
81
|
+
project: projectParam,
|
|
82
|
+
}),
|
|
83
|
+
}, async ({ domain, project }) => {
|
|
84
|
+
const d = domain ?? "all";
|
|
85
|
+
// topic domain requires a project
|
|
86
|
+
if (d === "topic") {
|
|
87
|
+
if (!project) {
|
|
88
|
+
return mcpResponse({ ok: false, error: "The 'topic' domain requires a project parameter." });
|
|
89
|
+
}
|
|
90
|
+
const err = validateProject(project);
|
|
91
|
+
if (err)
|
|
92
|
+
return mcpResponse({ ok: false, error: err });
|
|
93
|
+
const topicResult = getTopicConfigData(phrenPath, project);
|
|
94
|
+
if (!topicResult.ok)
|
|
95
|
+
return mcpResponse({ ok: false, error: topicResult.error });
|
|
96
|
+
return mcpResponse({
|
|
97
|
+
ok: true,
|
|
98
|
+
message: `Topic config for "${project}" (source: ${topicResult.data.source}).`,
|
|
99
|
+
data: topicResult.data,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (project) {
|
|
103
|
+
const err = validateProject(project);
|
|
104
|
+
if (err)
|
|
105
|
+
return mcpResponse({ ok: false, error: err });
|
|
106
|
+
const resolved = mergeConfig(phrenPath, project);
|
|
107
|
+
const projectOverrides = getProjectOverrides(phrenPath, project);
|
|
108
|
+
function src(key) {
|
|
109
|
+
return hasOwnOverride(projectOverrides, key) ? "project" : "global";
|
|
110
|
+
}
|
|
111
|
+
const result = {
|
|
112
|
+
_project: project,
|
|
113
|
+
_note: "Values marked _source=project override the global default.",
|
|
114
|
+
};
|
|
115
|
+
if (d === "all" || d === "findingSensitivity") {
|
|
116
|
+
const level = resolved.findingSensitivity;
|
|
117
|
+
result.findingSensitivity = {
|
|
118
|
+
level,
|
|
119
|
+
...FINDING_SENSITIVITY_CONFIG[level],
|
|
120
|
+
_source: src("findingSensitivity"),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (d === "all" || d === "taskMode") {
|
|
124
|
+
result.taskMode = { taskMode: resolved.taskMode, _source: src("taskMode") };
|
|
125
|
+
}
|
|
126
|
+
if (d === "all" || d === "retention") {
|
|
127
|
+
result.retention = {
|
|
128
|
+
...resolved.retentionPolicy,
|
|
129
|
+
_source: hasOwnOverride(projectOverrides, "retentionPolicy") ? "project" : "global",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (d === "all" || d === "workflow") {
|
|
133
|
+
result.workflow = {
|
|
134
|
+
...resolved.workflowPolicy,
|
|
135
|
+
_source: hasOwnOverride(projectOverrides, "workflowPolicy") ? "project" : "global",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (d === "all" || d === "proactivity") {
|
|
139
|
+
const globalSnapshot = proactivitySnapshot(phrenPath).effective;
|
|
140
|
+
const base = resolved.proactivity.base ?? globalSnapshot.proactivity;
|
|
141
|
+
const findings = resolved.proactivity.findings ?? resolved.proactivity.base ?? globalSnapshot.proactivityFindings;
|
|
142
|
+
const tasks = resolved.proactivity.tasks ?? resolved.proactivity.base ?? globalSnapshot.proactivityTask;
|
|
143
|
+
result.proactivity = {
|
|
144
|
+
base,
|
|
145
|
+
findings,
|
|
146
|
+
tasks,
|
|
147
|
+
_source: {
|
|
148
|
+
base: hasOwnOverride(projectOverrides, "proactivity") ? "project" : "global",
|
|
149
|
+
findings: hasOwnOverride(projectOverrides, "proactivityFindings")
|
|
150
|
+
? "project"
|
|
151
|
+
: hasOwnOverride(projectOverrides, "proactivity")
|
|
152
|
+
? "project"
|
|
153
|
+
: "global",
|
|
154
|
+
tasks: hasOwnOverride(projectOverrides, "proactivityTask")
|
|
155
|
+
? "project"
|
|
156
|
+
: hasOwnOverride(projectOverrides, "proactivity")
|
|
157
|
+
? "project"
|
|
158
|
+
: "global",
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (d === "all" || d === "index") {
|
|
163
|
+
result.index = getIndexPolicy(phrenPath);
|
|
164
|
+
}
|
|
165
|
+
return mcpResponse({
|
|
166
|
+
ok: true,
|
|
167
|
+
message: `Config for ${d === "all" ? "all domains" : d} (project: ${project}).`,
|
|
168
|
+
data: result,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const result = {};
|
|
172
|
+
if (d === "all" || d === "proactivity") {
|
|
173
|
+
result.proactivity = proactivitySnapshot(phrenPath);
|
|
174
|
+
}
|
|
175
|
+
if (d === "all" || d === "taskMode") {
|
|
176
|
+
const wf = getWorkflowPolicy(phrenPath);
|
|
177
|
+
result.taskMode = { taskMode: wf.taskMode };
|
|
178
|
+
}
|
|
179
|
+
if (d === "all" || d === "findingSensitivity") {
|
|
180
|
+
const wf = getWorkflowPolicy(phrenPath);
|
|
181
|
+
const level = wf.findingSensitivity;
|
|
182
|
+
const config = FINDING_SENSITIVITY_CONFIG[level];
|
|
183
|
+
result.findingSensitivity = { level, ...config };
|
|
184
|
+
}
|
|
185
|
+
if (d === "all" || d === "retention") {
|
|
186
|
+
result.retention = getRetentionPolicy(phrenPath);
|
|
187
|
+
}
|
|
188
|
+
if (d === "all" || d === "workflow") {
|
|
189
|
+
result.workflow = getWorkflowPolicy(phrenPath);
|
|
190
|
+
}
|
|
191
|
+
if (d === "all" || d === "index") {
|
|
192
|
+
result.index = getIndexPolicy(phrenPath);
|
|
193
|
+
}
|
|
194
|
+
return mcpResponse({
|
|
195
|
+
ok: true,
|
|
196
|
+
message: `Config for ${d === "all" ? "all domains" : d}.`,
|
|
197
|
+
data: result,
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
// ── set_config ──────────────────────────────────────────────────────────
|
|
201
|
+
server.registerTool("set_config", {
|
|
202
|
+
title: "◆ phren · set config",
|
|
203
|
+
description: "Update configuration for a specific domain. Replaces set_proactivity, set_task_mode, " +
|
|
204
|
+
"set_finding_sensitivity, set_retention_policy, set_workflow_policy, set_index_policy, " +
|
|
205
|
+
"and set_topic_config. When project is provided, writes to that project's phren.project.yaml " +
|
|
206
|
+
"instead of global .config/.",
|
|
207
|
+
inputSchema: z.object({
|
|
208
|
+
domain: z.enum(["proactivity", "taskMode", "findingSensitivity", "retention", "workflow", "index", "topic"]),
|
|
209
|
+
settings: z.record(z.string(), z.unknown()).describe("Domain-specific settings. proactivity: { level, scope? } | taskMode: { mode } | " +
|
|
210
|
+
"findingSensitivity: { level } | retention: { ttlDays?, retentionDays?, autoAcceptThreshold?, " +
|
|
211
|
+
"minInjectConfidence?, decay? } | workflow: { lowConfidenceThreshold?, riskySections?, taskMode?, " +
|
|
212
|
+
"findingSensitivity? } | index: { includeGlobs?, excludeGlobs?, includeHidden? } | " +
|
|
213
|
+
"topic: { topics, domain? }"),
|
|
214
|
+
project: z.string().optional().describe("Project name. When provided, writes to that project's phren.project.yaml instead of global .config/. " +
|
|
215
|
+
"Required for the 'topic' domain."),
|
|
216
|
+
}),
|
|
217
|
+
}, async ({ domain, settings, project }) => {
|
|
218
|
+
switch (domain) {
|
|
219
|
+
// ── proactivity ───────────────────────────────────────────────
|
|
220
|
+
case "proactivity": {
|
|
221
|
+
const level = settings.level;
|
|
222
|
+
if (!level || !PROACTIVITY_LEVELS.includes(level)) {
|
|
223
|
+
return mcpResponse({ ok: false, error: `Invalid proactivity level. Must be one of: ${PROACTIVITY_LEVELS.join(", ")}.` });
|
|
224
|
+
}
|
|
225
|
+
const scope = settings.scope ?? "base";
|
|
226
|
+
if (!["base", "findings", "tasks"].includes(scope)) {
|
|
227
|
+
return mcpResponse({ ok: false, error: `Invalid scope. Must be one of: base, findings, tasks.` });
|
|
228
|
+
}
|
|
229
|
+
const s = scope;
|
|
230
|
+
if (project) {
|
|
231
|
+
const err = validateProject(project);
|
|
232
|
+
if (err)
|
|
233
|
+
return mcpResponse({ ok: false, error: err });
|
|
234
|
+
const warning = checkProjectRegistered(phrenPath, project);
|
|
235
|
+
const key = s === "base" ? "proactivity" : s === "findings" ? "proactivityFindings" : "proactivityTask";
|
|
236
|
+
updateProjectConfigOverrides(phrenPath, project, (current) => ({
|
|
237
|
+
...current,
|
|
238
|
+
[key]: level,
|
|
239
|
+
}));
|
|
240
|
+
return mcpResponse({
|
|
241
|
+
ok: true,
|
|
242
|
+
message: warning
|
|
243
|
+
? `Proactivity ${s} set to ${level} for project "${project}". WARNING: ${warning}`
|
|
244
|
+
: `Proactivity ${s} set to ${level} for project "${project}".`,
|
|
245
|
+
data: { project, scope: s, level, ...(warning ? { warning } : {}) },
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
const patch = {};
|
|
249
|
+
if (s === "base")
|
|
250
|
+
patch.proactivity = level;
|
|
251
|
+
else if (s === "findings")
|
|
252
|
+
patch.proactivityFindings = level;
|
|
253
|
+
else if (s === "tasks")
|
|
254
|
+
patch.proactivityTask = level;
|
|
255
|
+
writeGovernanceInstallPreferences(phrenPath, patch);
|
|
256
|
+
return mcpResponse({
|
|
257
|
+
ok: true,
|
|
258
|
+
message: `Proactivity ${s} set to ${level}.`,
|
|
259
|
+
data: proactivitySnapshot(phrenPath),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
// ── taskMode ──────────────────────────────────────────────────
|
|
263
|
+
case "taskMode": {
|
|
264
|
+
const mode = settings.mode;
|
|
265
|
+
if (!mode || !VALID_TASK_MODES.includes(mode)) {
|
|
266
|
+
return mcpResponse({ ok: false, error: `Invalid task mode. Must be one of: ${VALID_TASK_MODES.join(", ")}.` });
|
|
267
|
+
}
|
|
268
|
+
const validMode = mode;
|
|
269
|
+
if (project) {
|
|
270
|
+
const err = validateProject(project);
|
|
271
|
+
if (err)
|
|
272
|
+
return mcpResponse({ ok: false, error: err });
|
|
273
|
+
const warning = checkProjectRegistered(phrenPath, project);
|
|
274
|
+
updateProjectConfigOverrides(phrenPath, project, (current) => ({
|
|
275
|
+
...current,
|
|
276
|
+
taskMode: validMode,
|
|
277
|
+
}));
|
|
278
|
+
return mcpResponse({
|
|
279
|
+
ok: true,
|
|
280
|
+
message: warning
|
|
281
|
+
? `Task mode set to ${validMode} for project "${project}". WARNING: ${warning}`
|
|
282
|
+
: `Task mode set to ${validMode} for project "${project}".`,
|
|
283
|
+
data: { project, taskMode: validMode, ...(warning ? { warning } : {}) },
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
const result = updateWorkflowPolicy(phrenPath, { taskMode: validMode });
|
|
287
|
+
if (!result.ok) {
|
|
288
|
+
return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
|
|
289
|
+
}
|
|
290
|
+
return mcpResponse({
|
|
291
|
+
ok: true,
|
|
292
|
+
message: `Task mode set to ${validMode}.`,
|
|
293
|
+
data: { taskMode: validMode },
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
// ── findingSensitivity ────────────────────────────────────────
|
|
297
|
+
case "findingSensitivity": {
|
|
298
|
+
const level = settings.level;
|
|
299
|
+
if (!level || !VALID_FINDING_SENSITIVITY.includes(level)) {
|
|
300
|
+
return mcpResponse({ ok: false, error: `Invalid finding sensitivity. Must be one of: ${VALID_FINDING_SENSITIVITY.join(", ")}.` });
|
|
301
|
+
}
|
|
302
|
+
const validLevel = level;
|
|
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
|
+
updateProjectConfigOverrides(phrenPath, project, (current) => ({
|
|
309
|
+
...current,
|
|
310
|
+
findingSensitivity: validLevel,
|
|
311
|
+
}));
|
|
312
|
+
const config = FINDING_SENSITIVITY_CONFIG[validLevel];
|
|
313
|
+
return mcpResponse({
|
|
314
|
+
ok: true,
|
|
315
|
+
message: warning
|
|
316
|
+
? `Finding sensitivity set to ${validLevel} for project "${project}". WARNING: ${warning}`
|
|
317
|
+
: `Finding sensitivity set to ${validLevel} for project "${project}".`,
|
|
318
|
+
data: { project, level: validLevel, ...config, ...(warning ? { warning } : {}) },
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
const result = updateWorkflowPolicy(phrenPath, { findingSensitivity: validLevel });
|
|
322
|
+
if (!result.ok) {
|
|
323
|
+
return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
|
|
324
|
+
}
|
|
325
|
+
const config = FINDING_SENSITIVITY_CONFIG[validLevel];
|
|
326
|
+
return mcpResponse({
|
|
327
|
+
ok: true,
|
|
328
|
+
message: `Finding sensitivity set to ${validLevel}.`,
|
|
329
|
+
data: { level: validLevel, ...config },
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
// ── retention ─────────────────────────────────────────────────
|
|
333
|
+
case "retention": {
|
|
334
|
+
const { ttlDays, retentionDays, autoAcceptThreshold, minInjectConfidence, decay } = settings;
|
|
335
|
+
if (project) {
|
|
336
|
+
const err = validateProject(project);
|
|
337
|
+
if (err)
|
|
338
|
+
return mcpResponse({ ok: false, error: err });
|
|
339
|
+
const warning = checkProjectRegistered(phrenPath, project);
|
|
340
|
+
const next = updateProjectConfigOverrides(phrenPath, project, (current) => {
|
|
341
|
+
const existingRetention = current.retentionPolicy ?? {};
|
|
342
|
+
const retentionPatch = { ...existingRetention };
|
|
343
|
+
if (ttlDays !== undefined)
|
|
344
|
+
retentionPatch.ttlDays = ttlDays;
|
|
345
|
+
if (retentionDays !== undefined)
|
|
346
|
+
retentionPatch.retentionDays = retentionDays;
|
|
347
|
+
if (autoAcceptThreshold !== undefined)
|
|
348
|
+
retentionPatch.autoAcceptThreshold = autoAcceptThreshold;
|
|
349
|
+
if (minInjectConfidence !== undefined)
|
|
350
|
+
retentionPatch.minInjectConfidence = minInjectConfidence;
|
|
351
|
+
if (decay !== undefined)
|
|
352
|
+
retentionPatch.decay = { ...(existingRetention.decay ?? {}), ...decay };
|
|
353
|
+
return { ...current, retentionPolicy: retentionPatch };
|
|
354
|
+
});
|
|
355
|
+
return mcpResponse({
|
|
356
|
+
ok: true,
|
|
357
|
+
message: warning
|
|
358
|
+
? `Retention policy updated for project "${project}". WARNING: ${warning}`
|
|
359
|
+
: `Retention policy updated for project "${project}".`,
|
|
360
|
+
data: { project, retentionPolicy: next.config?.retentionPolicy ?? {}, ...(warning ? { warning } : {}) },
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
const globalPatch = {};
|
|
364
|
+
if (ttlDays !== undefined)
|
|
365
|
+
globalPatch.ttlDays = ttlDays;
|
|
366
|
+
if (retentionDays !== undefined)
|
|
367
|
+
globalPatch.retentionDays = retentionDays;
|
|
368
|
+
if (autoAcceptThreshold !== undefined)
|
|
369
|
+
globalPatch.autoAcceptThreshold = autoAcceptThreshold;
|
|
370
|
+
if (minInjectConfidence !== undefined)
|
|
371
|
+
globalPatch.minInjectConfidence = minInjectConfidence;
|
|
372
|
+
if (decay !== undefined)
|
|
373
|
+
globalPatch.decay = decay;
|
|
374
|
+
const result = updateRetentionPolicy(phrenPath, globalPatch);
|
|
375
|
+
if (!result.ok) {
|
|
376
|
+
return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
|
|
377
|
+
}
|
|
378
|
+
return mcpResponse({
|
|
379
|
+
ok: true,
|
|
380
|
+
message: "Retention policy updated.",
|
|
381
|
+
data: result.data,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
// ── workflow ──────────────────────────────────────────────────
|
|
385
|
+
case "workflow": {
|
|
386
|
+
const { lowConfidenceThreshold, riskySections, taskMode, findingSensitivity } = settings;
|
|
387
|
+
if (project) {
|
|
388
|
+
const err = validateProject(project);
|
|
389
|
+
if (err)
|
|
390
|
+
return mcpResponse({ ok: false, error: err });
|
|
391
|
+
const warning = checkProjectRegistered(phrenPath, project);
|
|
392
|
+
const next = updateProjectConfigOverrides(phrenPath, project, (current) => {
|
|
393
|
+
const nextConfig = { ...current };
|
|
394
|
+
const shouldUpdateWorkflowPolicy = (lowConfidenceThreshold !== undefined
|
|
395
|
+
|| riskySections !== undefined
|
|
396
|
+
|| current.workflowPolicy !== undefined);
|
|
397
|
+
if (shouldUpdateWorkflowPolicy) {
|
|
398
|
+
const existingWorkflow = current.workflowPolicy ?? {};
|
|
399
|
+
nextConfig.workflowPolicy = {
|
|
400
|
+
...existingWorkflow,
|
|
401
|
+
...(lowConfidenceThreshold !== undefined ? { lowConfidenceThreshold } : {}),
|
|
402
|
+
...(riskySections !== undefined ? { riskySections: riskySections } : {}),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
if (taskMode !== undefined)
|
|
406
|
+
nextConfig.taskMode = taskMode;
|
|
407
|
+
if (findingSensitivity !== undefined)
|
|
408
|
+
nextConfig.findingSensitivity = findingSensitivity;
|
|
409
|
+
return nextConfig;
|
|
410
|
+
});
|
|
411
|
+
return mcpResponse({
|
|
412
|
+
ok: true,
|
|
413
|
+
message: warning
|
|
414
|
+
? `Workflow policy updated for project "${project}". WARNING: ${warning}`
|
|
415
|
+
: `Workflow policy updated for project "${project}".`,
|
|
416
|
+
data: { project, config: next.config ?? {}, ...(warning ? { warning } : {}) },
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
const patch = {};
|
|
420
|
+
if (lowConfidenceThreshold !== undefined)
|
|
421
|
+
patch.lowConfidenceThreshold = lowConfidenceThreshold;
|
|
422
|
+
if (riskySections !== undefined)
|
|
423
|
+
patch.riskySections = riskySections;
|
|
424
|
+
if (taskMode !== undefined)
|
|
425
|
+
patch.taskMode = taskMode;
|
|
426
|
+
if (findingSensitivity !== undefined)
|
|
427
|
+
patch.findingSensitivity = findingSensitivity;
|
|
428
|
+
const result = updateWorkflowPolicy(phrenPath, patch);
|
|
429
|
+
if (!result.ok) {
|
|
430
|
+
return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
|
|
431
|
+
}
|
|
432
|
+
return mcpResponse({
|
|
433
|
+
ok: true,
|
|
434
|
+
message: "Workflow policy updated.",
|
|
435
|
+
data: result.data,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
// ── index ─────────────────────────────────────────────────────
|
|
439
|
+
case "index": {
|
|
440
|
+
const { includeGlobs, excludeGlobs, includeHidden } = settings;
|
|
441
|
+
const patch = {};
|
|
442
|
+
if (includeGlobs !== undefined)
|
|
443
|
+
patch.includeGlobs = includeGlobs;
|
|
444
|
+
if (excludeGlobs !== undefined)
|
|
445
|
+
patch.excludeGlobs = excludeGlobs;
|
|
446
|
+
if (includeHidden !== undefined)
|
|
447
|
+
patch.includeHidden = includeHidden;
|
|
448
|
+
const result = updateIndexPolicy(phrenPath, patch);
|
|
449
|
+
if (!result.ok) {
|
|
450
|
+
return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
|
|
451
|
+
}
|
|
452
|
+
return mcpResponse({
|
|
453
|
+
ok: true,
|
|
454
|
+
message: "Index policy updated.",
|
|
455
|
+
data: result.data,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
// ── topic ─────────────────────────────────────────────────────
|
|
459
|
+
case "topic": {
|
|
460
|
+
if (!project) {
|
|
461
|
+
return mcpResponse({ ok: false, error: "The 'topic' domain requires a project parameter." });
|
|
462
|
+
}
|
|
463
|
+
const err = validateProject(project);
|
|
464
|
+
if (err)
|
|
465
|
+
return mcpResponse({ ok: false, error: err });
|
|
466
|
+
const projectDir = safeProjectPath(phrenPath, project);
|
|
467
|
+
if (!projectDir || !fs.existsSync(projectDir)) {
|
|
468
|
+
return mcpResponse({ ok: false, error: `Project "${project}" not found in phren.` });
|
|
469
|
+
}
|
|
470
|
+
const topics = settings.topics;
|
|
471
|
+
if (!topics || !Array.isArray(topics)) {
|
|
472
|
+
return mcpResponse({ ok: false, error: "The 'topic' domain requires a 'topics' array in settings." });
|
|
473
|
+
}
|
|
474
|
+
const topicDomain = settings.domain;
|
|
475
|
+
const normalized = topics.map((t) => ({
|
|
476
|
+
slug: t.slug,
|
|
477
|
+
label: t.label,
|
|
478
|
+
description: t.description ?? "",
|
|
479
|
+
keywords: t.keywords ?? [],
|
|
480
|
+
}));
|
|
481
|
+
// If a domain is provided, patch it onto the existing file before writing topics
|
|
482
|
+
if (topicDomain) {
|
|
483
|
+
const configPath = path.join(projectDir, "topic-config.json");
|
|
484
|
+
if (fs.existsSync(configPath)) {
|
|
485
|
+
try {
|
|
486
|
+
const existing = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
487
|
+
if (existing && typeof existing === "object") {
|
|
488
|
+
existing.domain = topicDomain;
|
|
489
|
+
fs.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
catch {
|
|
493
|
+
// ignore read errors; writeProjectTopics will still succeed
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
fs.mkdirSync(projectDir, { recursive: true });
|
|
498
|
+
fs.writeFileSync(configPath, JSON.stringify({ version: 1, domain: topicDomain, topics: [] }, null, 2) + "\n");
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const result = writeProjectTopics(phrenPath, project, normalized);
|
|
502
|
+
if (!result.ok) {
|
|
503
|
+
return mcpResponse({ ok: false, error: result.error });
|
|
504
|
+
}
|
|
505
|
+
return mcpResponse({
|
|
506
|
+
ok: true,
|
|
507
|
+
message: `Topic config written for "${project}" (${result.topics.length} topics).`,
|
|
508
|
+
data: { project, topics: result.topics, domain: topicDomain ?? null },
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
default:
|
|
512
|
+
return mcpResponse({ ok: false, error: `Unknown config domain: ${domain}` });
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { mcpResponse } from "./
|
|
1
|
+
import { mcpResponse } from "./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, errorMessage, safeProjectPath } from "
|
|
6
|
-
import { readFindings, readTasks, resolveTaskFilePath, TASKS_FILENAME } from "
|
|
7
|
-
import { debugLog, findArchivedProjectNameCaseInsensitive, findProjectNameCaseInsensitive, normalizeProjectNameForCreate } from "
|
|
5
|
+
import { isValidProjectName, errorMessage, safeProjectPath } from "../utils.js";
|
|
6
|
+
import { readFindings, readTasks, resolveTaskFilePath, TASKS_FILENAME } from "../data/access.js";
|
|
7
|
+
import { debugLog, findArchivedProjectNameCaseInsensitive, findProjectNameCaseInsensitive, normalizeProjectNameForCreate } from "../shared.js";
|
|
8
|
+
import { logger } from "../logger.js";
|
|
8
9
|
const importPayloadSchema = z.object({
|
|
9
10
|
project: z.string(),
|
|
10
11
|
overwrite: z.boolean().optional(),
|
|
@@ -77,8 +78,7 @@ export function register(server, ctx) {
|
|
|
77
78
|
decoded = JSON.parse(rawData);
|
|
78
79
|
}
|
|
79
80
|
catch (err) {
|
|
80
|
-
|
|
81
|
-
process.stderr.write(`[phren] import_project jsonParse: ${errorMessage(err)}\n`);
|
|
81
|
+
logger.debug("data", `import_project jsonParse: ${errorMessage(err)}`);
|
|
82
82
|
return mcpResponse({ ok: false, error: "Invalid JSON input." });
|
|
83
83
|
}
|
|
84
84
|
const parsedResult = importPayloadSchema.safeParse(decoded);
|
|
@@ -237,8 +237,7 @@ export function register(server, ctx) {
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
catch (err) {
|
|
240
|
-
|
|
241
|
-
process.stderr.write(`[phren] import_project backupRestore: ${errorMessage(err)}\n`);
|
|
240
|
+
logger.debug("data", `import_project backupRestore: ${errorMessage(err)}`);
|
|
242
241
|
}
|
|
243
242
|
}
|
|
244
243
|
return mcpResponse({
|
|
@@ -253,8 +252,7 @@ export function register(server, ctx) {
|
|
|
253
252
|
fs.rmSync(backupDir, { recursive: true, force: true });
|
|
254
253
|
}
|
|
255
254
|
catch (err) {
|
|
256
|
-
|
|
257
|
-
process.stderr.write(`[phren] import_project backupCleanup: ${errorMessage(err)}\n`);
|
|
255
|
+
logger.debug("data", `import_project backupCleanup: ${errorMessage(err)}`);
|
|
258
256
|
}
|
|
259
257
|
}
|
|
260
258
|
return mcpResponse({
|