@phren/cli 0.0.15 → 0.0.16
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 +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-graph.js +4 -1
- package/mcp/dist/cli-hooks-context.js +1 -1
- package/mcp/dist/cli-namespaces.js +17 -7
- package/mcp/dist/hooks.js +126 -26
- package/mcp/dist/mcp-data.js +38 -39
- package/mcp/dist/mcp-graph.js +2 -2
- package/mcp/dist/mcp-hooks.js +5 -64
- package/mcp/dist/mcp-skills.js +13 -5
- package/mcp/dist/memory-ui-assets.js +2 -1
- package/mcp/dist/memory-ui-graph.js +26 -12
- package/mcp/dist/memory-ui-page.js +2 -2
- package/mcp/dist/memory-ui-scripts.js +34 -18
- package/mcp/dist/memory-ui-server.js +12 -5
- package/mcp/dist/phren-paths.js +17 -0
- package/mcp/dist/shared.js +1 -1
- package/mcp/dist/skill-registry.js +25 -2
- package/package.json +1 -1
|
@@ -7,12 +7,12 @@ import * as querystring from "querystring";
|
|
|
7
7
|
import { spawn, execFileSync } from "child_process";
|
|
8
8
|
import { computePhrenLiveStateToken, getProjectDirs, } from "./shared.js";
|
|
9
9
|
import { editFinding, readReviewQueue, removeFinding, readFindings, addFinding as addFindingStore, readTasksAcrossProjects, addTask as addTaskStore, completeTask as completeTaskStore, removeTask as removeTaskStore, TASKS_FILENAME, } from "./data-access.js";
|
|
10
|
-
import { isValidProjectName, errorMessage, queueFilePath } from "./utils.js";
|
|
10
|
+
import { isValidProjectName, errorMessage, queueFilePath, safeProjectPath } from "./utils.js";
|
|
11
11
|
import { readInstallPreferences, writeInstallPreferences, writeGovernanceInstallPreferences } from "./init-preferences.js";
|
|
12
12
|
import { buildGraph, collectProjectsForUI, collectSkillsForUI, getHooksData, isAllowedFilePath, readSyncSnapshot, recentAccepted, recentUsage, } from "./memory-ui-data.js";
|
|
13
13
|
import { CONSOLIDATION_ENTRY_THRESHOLD } from "./content-validate.js";
|
|
14
14
|
import { ensureTopicReferenceDoc, getProjectTopicsResponse, listProjectReferenceDocs, pinProjectTopicSuggestion, readReferenceContent, reclassifyLegacyTopicDocs, unpinProjectTopicSuggestion, writeProjectTopics, } from "./project-topics.js";
|
|
15
|
-
import { getWorkflowPolicy, updateWorkflowPolicy, mergeConfig, getRetentionPolicy, getProjectConfigOverrides } from "./governance-policy.js";
|
|
15
|
+
import { getWorkflowPolicy, updateWorkflowPolicy, mergeConfig, getRetentionPolicy, getProjectConfigOverrides, VALID_TASK_MODES } from "./governance-policy.js";
|
|
16
16
|
import { updateProjectConfigOverrides } from "./project-config.js";
|
|
17
17
|
import { findSkill } from "./skill-registry.js";
|
|
18
18
|
import { setSkillEnabledAndSync } from "./skill-files.js";
|
|
@@ -193,6 +193,8 @@ function readFormBody(req, res) {
|
|
|
193
193
|
req.on("data", (chunk) => {
|
|
194
194
|
received += chunk.length;
|
|
195
195
|
if (received > MAX_FORM_BODY_BYTES) {
|
|
196
|
+
res.writeHead(413, { "Content-Type": "application/json" });
|
|
197
|
+
res.end(JSON.stringify({ ok: false, error: "Request body too large" }));
|
|
196
198
|
req.destroy();
|
|
197
199
|
resolve(null);
|
|
198
200
|
return;
|
|
@@ -508,7 +510,12 @@ export function createWebUiHttpServer(phrenPath, renderPage, profile, opts) {
|
|
|
508
510
|
res.end(JSON.stringify({ ok: false, error: `File not allowed: ${file}` }));
|
|
509
511
|
return;
|
|
510
512
|
}
|
|
511
|
-
const filePath =
|
|
513
|
+
const filePath = safeProjectPath(phrenPath, project, file);
|
|
514
|
+
if (!filePath) {
|
|
515
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
516
|
+
res.end(JSON.stringify({ ok: false, error: "Invalid project or file path" }));
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
512
519
|
if (!fs.existsSync(filePath)) {
|
|
513
520
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
514
521
|
res.end(JSON.stringify({ ok: false, error: `File not found: ${file}` }));
|
|
@@ -1024,7 +1031,7 @@ export function createWebUiHttpServer(phrenPath, renderPage, profile, opts) {
|
|
|
1024
1031
|
if (!requireCsrf(res, parsed, csrfTokens, true))
|
|
1025
1032
|
return;
|
|
1026
1033
|
const value = String(parsed.value || "").trim().toLowerCase();
|
|
1027
|
-
const valid =
|
|
1034
|
+
const valid = VALID_TASK_MODES;
|
|
1028
1035
|
if (!valid.includes(value)) {
|
|
1029
1036
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
1030
1037
|
res.end(JSON.stringify({ ok: false, error: `Invalid task mode: "${value}". Must be one of: ${valid.join(", ")}` }));
|
|
@@ -1161,7 +1168,7 @@ export function createWebUiHttpServer(phrenPath, renderPage, profile, opts) {
|
|
|
1161
1168
|
});
|
|
1162
1169
|
const merged = mergeConfig(phrenPath, project);
|
|
1163
1170
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
1164
|
-
res.end(JSON.stringify({ ok: true, config: merged }));
|
|
1171
|
+
res.end(JSON.stringify({ ok: true, config: merged, ...(registrationWarning ? { warning: registrationWarning } : {}) }));
|
|
1165
1172
|
}
|
|
1166
1173
|
catch (err) {
|
|
1167
1174
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
package/mcp/dist/phren-paths.js
CHANGED
|
@@ -305,6 +305,23 @@ export function findProjectNameCaseInsensitive(phrenPath, name) {
|
|
|
305
305
|
}
|
|
306
306
|
return null;
|
|
307
307
|
}
|
|
308
|
+
export function findArchivedProjectNameCaseInsensitive(phrenPath, name) {
|
|
309
|
+
const needle = name.toLowerCase();
|
|
310
|
+
try {
|
|
311
|
+
for (const entry of fs.readdirSync(phrenPath, { withFileTypes: true })) {
|
|
312
|
+
if (!entry.isDirectory() || !entry.name.endsWith(".archived"))
|
|
313
|
+
continue;
|
|
314
|
+
const archivedName = entry.name.slice(0, -".archived".length);
|
|
315
|
+
if (archivedName.toLowerCase() === needle)
|
|
316
|
+
return archivedName;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
if ((process.env.PHREN_DEBUG))
|
|
321
|
+
process.stderr.write(`[phren] findArchivedProjectNameCaseInsensitive: ${errorMessage(err)}\n`);
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
308
325
|
function getLocalProjectDirs(phrenPath, manifest) {
|
|
309
326
|
const primaryProject = manifest.primaryProject;
|
|
310
327
|
if (!primaryProject || !isValidProjectName(primaryProject))
|
package/mcp/dist/shared.js
CHANGED
|
@@ -4,7 +4,7 @@ import { debugLog, runtimeFile } from "./phren-paths.js";
|
|
|
4
4
|
import { errorMessage } from "./utils.js";
|
|
5
5
|
export { HOOK_TOOL_NAMES, hookConfigPath } from "./provider-adapters.js";
|
|
6
6
|
export { EXEC_TIMEOUT_MS, EXEC_TIMEOUT_QUICK_MS, PhrenError, phrenOk, phrenErr, forwardErr, parsePhrenErrorCode, isRecord, withDefaults, FINDING_TYPES, FINDING_TAGS, KNOWN_OBSERVATION_TAGS, DOC_TYPES, capCache, RESERVED_PROJECT_DIR_NAMES, } from "./phren-core.js";
|
|
7
|
-
export { ROOT_MANIFEST_FILENAME, homeDir, homePath, expandHomePath, defaultPhrenPath, rootManifestPath, readRootManifest, writeRootManifest, resolveInstallContext, findNearestPhrenPath, isProjectLocalMode, runtimeDir, tryUnlink, sessionsDir, runtimeFile, installPreferencesFile, runtimeHealthFile, shellStateFile, sessionMetricsFile, memoryScoresFile, memoryUsageLogFile, sessionMarker, debugLog, appendIndexEvent, resolveFindingsPath, findPhrenPath, ensurePhrenPath, findPhrenPathWithArg, normalizeProjectNameForCreate, findProjectNameCaseInsensitive, getProjectDirs, collectNativeMemoryFiles, computePhrenLiveStateToken, getPhrenPath, qualityMarkers, atomicWriteText, } from "./phren-paths.js";
|
|
7
|
+
export { ROOT_MANIFEST_FILENAME, homeDir, homePath, expandHomePath, defaultPhrenPath, rootManifestPath, readRootManifest, writeRootManifest, resolveInstallContext, findNearestPhrenPath, isProjectLocalMode, runtimeDir, tryUnlink, sessionsDir, runtimeFile, installPreferencesFile, runtimeHealthFile, shellStateFile, sessionMetricsFile, memoryScoresFile, memoryUsageLogFile, sessionMarker, debugLog, appendIndexEvent, resolveFindingsPath, findPhrenPath, ensurePhrenPath, findPhrenPathWithArg, normalizeProjectNameForCreate, findProjectNameCaseInsensitive, findArchivedProjectNameCaseInsensitive, getProjectDirs, collectNativeMemoryFiles, computePhrenLiveStateToken, getPhrenPath, qualityMarkers, atomicWriteText, } from "./phren-paths.js";
|
|
8
8
|
export { PROACTIVITY_LEVELS, getProactivityLevel, getProactivityLevelForFindings, getProactivityLevelForTask, hasExplicitFindingSignal, hasExplicitTaskSignal, hasExecutionIntent, hasDiscoveryIntent, shouldAutoCaptureFindingsForLevel, shouldAutoCaptureTaskForLevel, } from "./proactivity.js";
|
|
9
9
|
const MEMORY_SCOPE_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
10
10
|
export function normalizeMemoryScope(scope) {
|
|
@@ -3,6 +3,7 @@ import * as path from "path";
|
|
|
3
3
|
import { getProjectDirs } from "./shared.js";
|
|
4
4
|
import { parseSkillFrontmatter } from "./link-skills.js";
|
|
5
5
|
import { isSkillEnabled } from "./skill-state.js";
|
|
6
|
+
import { safeProjectPath } from "./utils.js";
|
|
6
7
|
function normalizeCommand(raw, fallbackName) {
|
|
7
8
|
const value = typeof raw === "string" && raw.trim() ? raw.trim() : `/${fallbackName}`;
|
|
8
9
|
return value.startsWith("/") ? value : `/${value}`;
|
|
@@ -28,13 +29,35 @@ function collectSkills(phrenPath, root, sourceLabel, scopeType, sourceKind, seen
|
|
|
28
29
|
if (!fs.existsSync(root))
|
|
29
30
|
return [];
|
|
30
31
|
const results = [];
|
|
32
|
+
const realRoot = (() => {
|
|
33
|
+
try {
|
|
34
|
+
return fs.realpathSync(root);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return path.resolve(root);
|
|
38
|
+
}
|
|
39
|
+
})();
|
|
31
40
|
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
32
41
|
const isFolder = entry.isDirectory();
|
|
33
|
-
const
|
|
42
|
+
const candidatePath = isFolder
|
|
34
43
|
? path.join(root, entry.name, "SKILL.md")
|
|
35
44
|
: entry.name.endsWith(".md") ? path.join(root, entry.name) : null;
|
|
36
|
-
if (!
|
|
45
|
+
if (!candidatePath)
|
|
46
|
+
continue;
|
|
47
|
+
const safeCandidate = safeProjectPath(root, path.relative(root, candidatePath));
|
|
48
|
+
if (!safeCandidate || seen.has(safeCandidate) || !fs.existsSync(safeCandidate))
|
|
37
49
|
continue;
|
|
50
|
+
try {
|
|
51
|
+
if (fs.lstatSync(safeCandidate).isSymbolicLink())
|
|
52
|
+
continue;
|
|
53
|
+
const realCandidate = fs.realpathSync(safeCandidate);
|
|
54
|
+
if (realCandidate !== realRoot && !realCandidate.startsWith(realRoot + path.sep))
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const filePath = safeCandidate;
|
|
38
61
|
seen.add(filePath);
|
|
39
62
|
const name = isFolder ? entry.name : entry.name.replace(/\.md$/, "");
|
|
40
63
|
const { frontmatter } = parseSkillFrontmatter(fs.readFileSync(filePath, "utf8"));
|