@phren/cli 0.0.32 → 0.0.34
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 +52 -6
- package/mcp/dist/index.js +1 -1
- package/mcp/dist/init/init.js +66 -45
- 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/link/doctor.js +9 -0
- 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
package/mcp/dist/shared/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import { buildSourceDocKey, queryDocBySourceKey, queryDocRows, } from "../index-
|
|
|
17
17
|
import { classifyTopicForText, readProjectTopics, } from "../project-topics.js";
|
|
18
18
|
export { porterStem } from "./stemmer.js";
|
|
19
19
|
export { cosineFallback } from "./search-fallback.js";
|
|
20
|
-
export { queryFragmentLinks,
|
|
20
|
+
export { queryFragmentLinks, getFragmentBoostDocs, ensureGlobalEntitiesTable, queryCrossProjectFragments, logFragmentMiss, extractFragmentNames, } from "./fragment-graph.js";
|
|
21
21
|
export { buildSourceDocKey, decodeFiniteNumber, decodeStringRow, extractSnippet, getDocSourceKey, normalizeMemoryId, queryDocBySourceKey, queryDocRows, queryRows, rowToDoc, rowToDocWithRowid, } from "../index-query.js";
|
|
22
22
|
// ── Async embedding queue ───────────────────────────────────────────────────
|
|
23
23
|
const _embQueue = new Map();
|
|
@@ -165,6 +165,7 @@ function _resolveImportsRecursive(content, phrenPath, seen, depth) {
|
|
|
165
165
|
* The import path is resolved relative to the phren root (e.g. `shared/foo.md` -> `~/.phren/global/shared/foo.md`).
|
|
166
166
|
* Circular imports are detected and skipped. Depth is capped to prevent runaway recursion.
|
|
167
167
|
*/
|
|
168
|
+
/** @internal Exported for tests. */
|
|
168
169
|
export function resolveImports(content, phrenPath) {
|
|
169
170
|
return _resolveImportsRecursive(content, phrenPath, new Set(), 0);
|
|
170
171
|
}
|
|
@@ -324,7 +325,7 @@ function loadHashMap(phrenPath) {
|
|
|
324
325
|
}
|
|
325
326
|
return { hashes: {} };
|
|
326
327
|
}
|
|
327
|
-
function saveHashMap(phrenPath, hashes) {
|
|
328
|
+
function saveHashMap(phrenPath, hashes, knownPaths) {
|
|
328
329
|
const runtimeDir = path.join(phrenPath, ".runtime");
|
|
329
330
|
try {
|
|
330
331
|
fs.mkdirSync(runtimeDir, { recursive: true });
|
|
@@ -343,9 +344,11 @@ function saveHashMap(phrenPath, hashes) {
|
|
|
343
344
|
logger.debug("saveHashMap readExisting", errorMessage(err));
|
|
344
345
|
}
|
|
345
346
|
const merged = { ...existing, ...hashes };
|
|
346
|
-
// Remove entries for paths that no longer exist
|
|
347
|
+
// Remove entries for paths that no longer exist. When knownPaths is provided
|
|
348
|
+
// (build passes supply the full file list), use set membership instead of
|
|
349
|
+
// hitting the filesystem — avoids N sync stat calls inside the lock.
|
|
347
350
|
for (const filePath of Object.keys(merged)) {
|
|
348
|
-
if (!fs.existsSync(filePath)) {
|
|
351
|
+
if (knownPaths ? !knownPaths.has(filePath) : !fs.existsSync(filePath)) {
|
|
349
352
|
delete merged[filePath];
|
|
350
353
|
}
|
|
351
354
|
}
|
|
@@ -815,7 +818,8 @@ function mergeManualLinks(db, phrenPath) {
|
|
|
815
818
|
}
|
|
816
819
|
async function buildIndexImpl(phrenPath, profile) {
|
|
817
820
|
const t0 = Date.now();
|
|
818
|
-
|
|
821
|
+
const projectDirs = getProjectDirs(phrenPath, profile);
|
|
822
|
+
beginUserFragmentBuildCache(phrenPath, projectDirs.map(dir => path.basename(dir)));
|
|
819
823
|
try {
|
|
820
824
|
// ── Cache dir + hash sentinel ─────────────────────────────────────────────
|
|
821
825
|
let userSuffix;
|
|
@@ -1021,7 +1025,7 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
1021
1025
|
throw err;
|
|
1022
1026
|
}
|
|
1023
1027
|
}
|
|
1024
|
-
saveHashMap(phrenPath, currentHashes);
|
|
1028
|
+
saveHashMap(phrenPath, currentHashes, new Set(Object.keys(currentHashes)));
|
|
1025
1029
|
touchSentinel(phrenPath);
|
|
1026
1030
|
invalidateDfCache();
|
|
1027
1031
|
// Save updated cache
|
|
@@ -1120,19 +1124,19 @@ async function buildIndexImpl(phrenPath, profile) {
|
|
|
1120
1124
|
// Always merge manual links (survive rebuild)
|
|
1121
1125
|
mergeManualLinks(db, phrenPath);
|
|
1122
1126
|
// ── Finalize: persist hashes, save cache, log ─────────────────────────────
|
|
1123
|
-
saveHashMap(phrenPath, newHashes);
|
|
1127
|
+
saveHashMap(phrenPath, newHashes, new Set(Object.keys(newHashes)));
|
|
1124
1128
|
touchSentinel(phrenPath);
|
|
1125
1129
|
invalidateDfCache();
|
|
1126
1130
|
const buildMs = Date.now() - t0;
|
|
1127
|
-
debugLog(`Built FTS index: ${fileCount} files from ${
|
|
1131
|
+
debugLog(`Built FTS index: ${fileCount} files from ${projectDirs.length} projects in ${buildMs}ms`);
|
|
1128
1132
|
if ((process.env.PHREN_DEBUG))
|
|
1129
|
-
console.error(`Indexed ${fileCount} files from ${
|
|
1133
|
+
console.error(`Indexed ${fileCount} files from ${projectDirs.length} projects`);
|
|
1130
1134
|
appendIndexEvent(phrenPath, {
|
|
1131
1135
|
event: "build_index",
|
|
1132
1136
|
cache: "miss",
|
|
1133
1137
|
hash: hash.slice(0, 12),
|
|
1134
1138
|
files: fileCount,
|
|
1135
|
-
projects:
|
|
1139
|
+
projects: projectDirs.length,
|
|
1136
1140
|
elapsedMs: buildMs,
|
|
1137
1141
|
profile: profile || "",
|
|
1138
1142
|
});
|
|
@@ -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.
|