@phren/cli 0.0.32 → 0.0.33
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/cli/actions.js +3 -0
- package/mcp/dist/cli/config.js +3 -3
- package/mcp/dist/cli/govern.js +18 -8
- package/mcp/dist/cli/hooks-context.js +1 -1
- package/mcp/dist/cli/hooks-session.js +18 -62
- package/mcp/dist/cli/namespaces.js +1 -1
- package/mcp/dist/cli/search.js +5 -5
- package/mcp/dist/cli-hooks-prompt.js +7 -3
- package/mcp/dist/cli-hooks-session-handlers.js +3 -15
- package/mcp/dist/cli-hooks-stop.js +10 -48
- package/mcp/dist/content/archive.js +8 -20
- package/mcp/dist/content/learning.js +29 -8
- package/mcp/dist/data/access.js +13 -4
- package/mcp/dist/finding/lifecycle.js +9 -3
- package/mcp/dist/governance/audit.js +13 -5
- package/mcp/dist/governance/policy.js +13 -0
- package/mcp/dist/governance/rbac.js +1 -1
- package/mcp/dist/governance/scores.js +2 -1
- package/mcp/dist/hooks.js +14 -6
- package/mcp/dist/index.js +1 -1
- package/mcp/dist/init/init.js +54 -42
- package/mcp/dist/init/shared.js +1 -1
- package/mcp/dist/init-bootstrap.js +0 -47
- package/mcp/dist/init-fresh.js +13 -18
- package/mcp/dist/init-uninstall.js +22 -0
- package/mcp/dist/init-walkthrough.js +19 -24
- package/mcp/dist/package-metadata.js +1 -1
- package/mcp/dist/phren-art.js +4 -120
- package/mcp/dist/proactivity.js +1 -1
- package/mcp/dist/project-topics.js +16 -46
- package/mcp/dist/provider-adapters.js +1 -1
- package/mcp/dist/runtime-profile.js +1 -1
- package/mcp/dist/shared/data-utils.js +25 -0
- package/mcp/dist/shared/fragment-graph.js +4 -18
- package/mcp/dist/shared/index.js +14 -10
- package/mcp/dist/shared/ollama.js +23 -5
- package/mcp/dist/shared/process.js +24 -0
- package/mcp/dist/shared/retrieval.js +7 -4
- package/mcp/dist/shared/search-fallback.js +1 -0
- package/mcp/dist/shared.js +2 -1
- package/mcp/dist/shell/render.js +1 -1
- package/mcp/dist/skill/registry.js +1 -1
- package/mcp/dist/skill/state.js +0 -3
- package/mcp/dist/task/github.js +1 -0
- package/mcp/dist/task/lifecycle.js +1 -6
- package/mcp/dist/tools/config.js +415 -400
- package/mcp/dist/tools/finding.js +390 -373
- package/mcp/dist/tools/ops.js +372 -365
- package/mcp/dist/tools/search.js +495 -487
- package/mcp/dist/tools/session.js +3 -2
- package/mcp/dist/tools/skills.js +9 -0
- package/mcp/dist/ui/page.js +1 -1
- package/mcp/dist/ui/server.js +645 -1040
- package/mcp/dist/utils.js +12 -8
- package/package.json +1 -1
- package/mcp/dist/init-dryrun.js +0 -55
- package/mcp/dist/init-migrate.js +0 -51
- package/mcp/dist/init-walkthrough-merge.js +0 -90
|
@@ -3,6 +3,11 @@ const DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
|
3
3
|
const DEFAULT_EMBEDDING_MODEL = "nomic-embed-text";
|
|
4
4
|
const DEFAULT_EXTRACT_MODEL = "llama3.2";
|
|
5
5
|
const MAX_EMBED_INPUT_CHARS = 6000;
|
|
6
|
+
const CLOUD_EMBEDDING_TIMEOUT_MS = 15_000;
|
|
7
|
+
const OLLAMA_HEALTH_TIMEOUT_MS = 2_000;
|
|
8
|
+
const OLLAMA_EMBEDDING_TIMEOUT_MS = 10_000;
|
|
9
|
+
const OLLAMA_GENERATE_TIMEOUT_MS = 60_000;
|
|
10
|
+
/** @internal Exported for tests. */
|
|
6
11
|
export function prepareEmbeddingInput(text) {
|
|
7
12
|
return text
|
|
8
13
|
.replace(/<!--[\s\S]*?-->/g, " ")
|
|
@@ -44,7 +49,7 @@ async function embedTextCloud(input, baseUrl, model, apiKey) {
|
|
|
44
49
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
45
50
|
try {
|
|
46
51
|
const controller = new AbortController();
|
|
47
|
-
const id = setTimeout(() => controller.abort(),
|
|
52
|
+
const id = setTimeout(() => controller.abort(), CLOUD_EMBEDDING_TIMEOUT_MS);
|
|
48
53
|
const res = await fetch(`${baseUrl}/embeddings`, {
|
|
49
54
|
method: "POST",
|
|
50
55
|
headers,
|
|
@@ -85,7 +90,7 @@ export async function checkOllamaAvailable(url) {
|
|
|
85
90
|
return false;
|
|
86
91
|
try {
|
|
87
92
|
const controller = new AbortController();
|
|
88
|
-
const id = setTimeout(() => controller.abort(),
|
|
93
|
+
const id = setTimeout(() => controller.abort(), OLLAMA_HEALTH_TIMEOUT_MS);
|
|
89
94
|
const res = await fetch(`${baseUrl}/api/tags`, { signal: controller.signal });
|
|
90
95
|
clearTimeout(id);
|
|
91
96
|
return res.ok;
|
|
@@ -104,7 +109,7 @@ export async function checkModelAvailable(model, url) {
|
|
|
104
109
|
const modelName = model ?? getEmbeddingModel();
|
|
105
110
|
try {
|
|
106
111
|
const controller = new AbortController();
|
|
107
|
-
const id = setTimeout(() => controller.abort(),
|
|
112
|
+
const id = setTimeout(() => controller.abort(), OLLAMA_HEALTH_TIMEOUT_MS);
|
|
108
113
|
const res = await fetch(`${baseUrl}/api/tags`, { signal: controller.signal });
|
|
109
114
|
clearTimeout(id);
|
|
110
115
|
if (!res.ok)
|
|
@@ -131,7 +136,7 @@ export async function embedText(text, model, url) {
|
|
|
131
136
|
return null;
|
|
132
137
|
try {
|
|
133
138
|
const controller = new AbortController();
|
|
134
|
-
const id = setTimeout(() => controller.abort(),
|
|
139
|
+
const id = setTimeout(() => controller.abort(), OLLAMA_EMBEDDING_TIMEOUT_MS);
|
|
135
140
|
const res = await fetch(`${baseUrl}/api/embed`, {
|
|
136
141
|
method: "POST",
|
|
137
142
|
headers: { "Content-Type": "application/json" },
|
|
@@ -158,7 +163,7 @@ export async function generateText(prompt, model, url) {
|
|
|
158
163
|
const modelName = model ?? getExtractModel();
|
|
159
164
|
try {
|
|
160
165
|
const controller = new AbortController();
|
|
161
|
-
const id = setTimeout(() => controller.abort(),
|
|
166
|
+
const id = setTimeout(() => controller.abort(), OLLAMA_GENERATE_TIMEOUT_MS);
|
|
162
167
|
const res = await fetch(`${baseUrl}/api/generate`, {
|
|
163
168
|
method: "POST",
|
|
164
169
|
headers: { "Content-Type": "application/json" },
|
|
@@ -178,4 +183,17 @@ export async function generateText(prompt, model, url) {
|
|
|
178
183
|
return null;
|
|
179
184
|
}
|
|
180
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Probe Ollama availability and model readiness in one call.
|
|
188
|
+
* Returns a status enum so callers can branch on it without repeating the check logic.
|
|
189
|
+
*/
|
|
190
|
+
export async function checkOllamaStatus() {
|
|
191
|
+
if (!getOllamaUrl())
|
|
192
|
+
return "disabled";
|
|
193
|
+
const ollamaUp = await checkOllamaAvailable();
|
|
194
|
+
if (!ollamaUp)
|
|
195
|
+
return "not_running";
|
|
196
|
+
const modelReady = await checkModelAvailable();
|
|
197
|
+
return modelReady ? "ready" : "no_model";
|
|
198
|
+
}
|
|
181
199
|
export { cosineSimilarity } from "../embedding.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared process helpers for spawning detached child processes.
|
|
3
|
+
*/
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import { resolveRuntimeProfile } from "../runtime-profile.js";
|
|
6
|
+
/**
|
|
7
|
+
* Spawn a detached child process with the standard phren environment.
|
|
8
|
+
* When logFd is provided, stdout/stderr are redirected to that fd.
|
|
9
|
+
* When omitted, all stdio is ignored.
|
|
10
|
+
* Returns the ChildProcess so callers can attach `.unref()` or `.on("exit", ...)`.
|
|
11
|
+
*/
|
|
12
|
+
export function spawnDetachedChild(args, opts) {
|
|
13
|
+
return spawn(process.execPath, args, {
|
|
14
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
15
|
+
detached: true,
|
|
16
|
+
stdio: opts.logFd !== undefined ? ["ignore", opts.logFd, opts.logFd] : "ignore",
|
|
17
|
+
env: {
|
|
18
|
+
...process.env,
|
|
19
|
+
PHREN_PATH: opts.phrenPath,
|
|
20
|
+
PHREN_PROFILE: resolveRuntimeProfile(opts.phrenPath),
|
|
21
|
+
...opts.extraEnv,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// shared-retrieval.ts — shared retrieval core used by hooks and MCP search.
|
|
2
2
|
import { getQualityMultiplier, entryScoreKey, } from "./governance.js";
|
|
3
|
-
import { queryDocRows, queryRows, cosineFallback, extractSnippet, getDocSourceKey,
|
|
3
|
+
import { queryDocRows, queryRows, cosineFallback, extractSnippet, getDocSourceKey, getFragmentBoostDocs, decodeFiniteNumber, rowToDocWithRowid, buildIndex, } from "./index.js";
|
|
4
4
|
import { filterTrustedFindingsDetailed, } from "./content.js";
|
|
5
5
|
import { parseCitationComment } from "../content/citation.js";
|
|
6
6
|
import { getHighImpactFindings } from "../finding/impact.js";
|
|
@@ -183,6 +183,7 @@ const RRF_K = 60;
|
|
|
183
183
|
* Documents appearing in multiple tiers get a higher combined score.
|
|
184
184
|
* Formula: score(d) = Σ 1/(k + rank_i) for each tier i containing d, where k=60 (standard).
|
|
185
185
|
*/
|
|
186
|
+
/** @internal Exported for tests. */
|
|
186
187
|
export function rrfMerge(tiers, k = RRF_K) {
|
|
187
188
|
const scores = new Map();
|
|
188
189
|
const docs = new Map();
|
|
@@ -267,6 +268,7 @@ function semanticFallbackDocs(db, prompt, project) {
|
|
|
267
268
|
.map((x) => x.doc);
|
|
268
269
|
return scored;
|
|
269
270
|
}
|
|
271
|
+
/** @internal Exported for tests. */
|
|
270
272
|
export function shouldRunVectorExpansion(rows, prompt, desiredResults = VECTOR_FALLBACK_SKIP_COUNT) {
|
|
271
273
|
if (!rows || rows.length === 0)
|
|
272
274
|
return true;
|
|
@@ -492,7 +494,7 @@ export async function searchKnowledgeRows(db, options) {
|
|
|
492
494
|
* Parse PHREN_FEDERATION_PATHS env var and return valid, distinct paths.
|
|
493
495
|
* Paths are colon-separated. The local phrenPath is excluded to avoid duplicate results.
|
|
494
496
|
*/
|
|
495
|
-
|
|
497
|
+
function parseFederationPaths(localPhrenPath) {
|
|
496
498
|
const raw = process.env.PHREN_FEDERATION_PATHS ?? "";
|
|
497
499
|
if (!raw.trim())
|
|
498
500
|
return [];
|
|
@@ -624,7 +626,7 @@ export function rankResults(rows, intent, gitCtx, detectedProject, phrenPathLoca
|
|
|
624
626
|
if (canonicalRows)
|
|
625
627
|
ranked = [...canonicalRows, ...ranked];
|
|
626
628
|
}
|
|
627
|
-
const entityBoost = query ?
|
|
629
|
+
const entityBoost = query ? getFragmentBoostDocs(db, query) : new Set();
|
|
628
630
|
const entityBoostPaths = new Set();
|
|
629
631
|
for (const doc of ranked) {
|
|
630
632
|
// Use getDocSourceKey to build the full project/relFile key, matching what
|
|
@@ -735,7 +737,8 @@ export function rankResults(rows, intent, gitCtx, detectedProject, phrenPathLoca
|
|
|
735
737
|
}
|
|
736
738
|
return ranked;
|
|
737
739
|
}
|
|
738
|
-
/** Mark snippet lines with stale citations (cited file missing or line content changed).
|
|
740
|
+
/** Mark snippet lines with stale citations (cited file missing or line content changed).
|
|
741
|
+
* @internal Exported for tests. */
|
|
739
742
|
export function markStaleCitations(snippet) {
|
|
740
743
|
const lines = snippet.split("\n");
|
|
741
744
|
const result = [];
|
|
@@ -17,6 +17,7 @@ const COSINE_WINDOW_COUNT = 4;
|
|
|
17
17
|
function splitPathSegments(filePath) {
|
|
18
18
|
return filePath.split(/[\\/]+/).filter(Boolean);
|
|
19
19
|
}
|
|
20
|
+
/** @internal Exported for tests. */
|
|
20
21
|
export function deriveVectorDocIdentity(phrenPath, fullPath) {
|
|
21
22
|
const normalizedPhrenPath = phrenPath.replace(/[\\/]+/g, "/").replace(/\/+$/, "");
|
|
22
23
|
const normalizedFullPath = fullPath.replace(/[\\/]+/g, "/");
|
package/mcp/dist/shared.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as path from "path";
|
|
|
3
3
|
import { debugLog, runtimeFile } from "./phren-paths.js";
|
|
4
4
|
import { errorMessage } from "./utils.js";
|
|
5
5
|
import { withFileLock } from "./governance/locks.js";
|
|
6
|
+
const MAX_LOG_LINES = 1000;
|
|
6
7
|
export { HOOK_TOOL_NAMES, hookConfigPath } from "./provider-adapters.js";
|
|
7
8
|
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";
|
|
8
9
|
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";
|
|
@@ -39,7 +40,7 @@ export function appendAuditLog(phrenPath, event, details) {
|
|
|
39
40
|
if (stat.size > 1_000_000) {
|
|
40
41
|
const content = fs.readFileSync(logPath, "utf8");
|
|
41
42
|
const lines = content.split("\n");
|
|
42
|
-
fs.writeFileSync(logPath, lines.slice(-
|
|
43
|
+
fs.writeFileSync(logPath, lines.slice(-MAX_LOG_LINES).join("\n") + "\n");
|
|
43
44
|
}
|
|
44
45
|
});
|
|
45
46
|
}
|
package/mcp/dist/shell/render.js
CHANGED
|
@@ -37,7 +37,7 @@ export function separator(width = 50) {
|
|
|
37
37
|
export function stripAnsi(s) {
|
|
38
38
|
return s.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
function visibleWidth(s) {
|
|
41
41
|
return stripAnsi(s).length;
|
|
42
42
|
}
|
|
43
43
|
export function padToWidth(s, width) {
|
|
@@ -223,7 +223,7 @@ export function getAllSkills(phrenPath, profile) {
|
|
|
223
223
|
}
|
|
224
224
|
return all;
|
|
225
225
|
}
|
|
226
|
-
|
|
226
|
+
function getLocalSkills(phrenPath, scope) {
|
|
227
227
|
if (scope.toLowerCase() === "global")
|
|
228
228
|
return getGlobalSkills(phrenPath);
|
|
229
229
|
return getProjectLocalSkills(phrenPath, scope);
|
package/mcp/dist/skill/state.js
CHANGED
package/mcp/dist/task/github.js
CHANGED
|
@@ -80,7 +80,7 @@ function clearTaskSessionState(phrenPath, sessionId) {
|
|
|
80
80
|
debugLog(`task lifecycle clear session ${sessionId}: ${errorMessage(err)}`);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
function getTaskMode(phrenPath) {
|
|
84
84
|
return getWorkflowPolicy(phrenPath).taskMode;
|
|
85
85
|
}
|
|
86
86
|
function isActionablePrompt(prompt, intent) {
|
|
@@ -330,11 +330,6 @@ export function finalizeTaskSession(args) {
|
|
|
330
330
|
return;
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
|
-
export function clearTaskSession(phrenPath, sessionId) {
|
|
334
|
-
if (!sessionId)
|
|
335
|
-
return;
|
|
336
|
-
clearTaskSessionState(phrenPath, sessionId);
|
|
337
|
-
}
|
|
338
333
|
/**
|
|
339
334
|
* Return the active TaskItem tracked for a session+project, if any.
|
|
340
335
|
* Used by mcp-finding.ts to link findings to active tasks.
|