@phren/cli 0.0.50 → 0.0.52
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 +17 -2
- package/mcp/dist/cli/cli.js +1 -1
- package/mcp/dist/cli/namespaces.js +35 -10
- package/mcp/dist/cli/ops.js +2 -2
- package/mcp/dist/content/validate.js +18 -0
- package/mcp/dist/data/access.js +27 -0
- package/mcp/dist/data/tasks.js +27 -2
- package/mcp/dist/generated/memory-ui-graph.browser.js +21 -21
- package/mcp/dist/governance/policy.js +3 -1
- package/mcp/dist/memory-ui-graph.runtime.js +21 -21
- package/mcp/dist/phren-core.js +1 -1
- package/mcp/dist/profile-store.js +20 -0
- package/mcp/dist/project-config.js +26 -0
- package/mcp/dist/shared/index.js +1 -1
- package/mcp/dist/shell/view.js +27 -9
- package/mcp/dist/store-routing.js +2 -2
- package/mcp/dist/task/lifecycle.js +11 -0
- package/mcp/dist/tools/config.js +23 -5
- package/mcp/dist/tools/data.js +17 -7
- package/mcp/dist/tools/extract.js +8 -5
- package/mcp/dist/tools/finding.js +14 -9
- package/mcp/dist/tools/graph.js +12 -3
- package/mcp/dist/tools/hooks.js +15 -2
- package/mcp/dist/tools/session.js +58 -18
- package/mcp/dist/tools/tasks.js +58 -44
- package/mcp/dist/ui/data.js +46 -19
- package/mcp/dist/ui/server.js +2 -1
- package/package.json +1 -1
package/mcp/dist/tools/tasks.js
CHANGED
|
@@ -109,7 +109,8 @@ export function register(server, ctx) {
|
|
|
109
109
|
return mcpResponse({ ok: false, error: "Provide `project` when looking up a single item." });
|
|
110
110
|
if (!isValidProjectName(project))
|
|
111
111
|
return mcpResponse({ ok: false, error: `Invalid project name: "${project}"` });
|
|
112
|
-
const
|
|
112
|
+
const resolvedPath = resolveStoreForProject(ctx, project).phrenPath;
|
|
113
|
+
const result = readTasks(resolvedPath, project);
|
|
113
114
|
if (!result.ok)
|
|
114
115
|
return mcpResponse({ ok: false, error: result.error });
|
|
115
116
|
const doc = result.data;
|
|
@@ -141,7 +142,8 @@ export function register(server, ctx) {
|
|
|
141
142
|
if (project) {
|
|
142
143
|
if (!isValidProjectName(project))
|
|
143
144
|
return mcpResponse({ ok: false, error: `Invalid project name: "${project}"` });
|
|
144
|
-
const
|
|
145
|
+
const resolvedPath = resolveStoreForProject(ctx, project).phrenPath;
|
|
146
|
+
const result = readTasks(resolvedPath, project);
|
|
145
147
|
if (!result.ok)
|
|
146
148
|
return mcpResponse({ ok: false, error: result.error });
|
|
147
149
|
const doc = result.data;
|
|
@@ -257,21 +259,24 @@ export function register(server, ctx) {
|
|
|
257
259
|
]).describe("The task(s) to complete. Pass a string for one, or an array for bulk."),
|
|
258
260
|
sessionId: z.string().optional().describe("Optional session ID from session_start. Pass this to track per-session task completion metrics."),
|
|
259
261
|
}),
|
|
260
|
-
}, async ({ project, item, sessionId }) => {
|
|
262
|
+
}, async ({ project: projectInput, item, sessionId }) => {
|
|
263
|
+
const resolved = resolveStoreForProject(ctx, projectInput);
|
|
264
|
+
const project = resolved.project;
|
|
265
|
+
const targetPath = resolved.phrenPath;
|
|
261
266
|
if (!isValidProjectName(project))
|
|
262
267
|
return mcpResponse({ ok: false, error: `Invalid project name: "${project}"` });
|
|
263
|
-
const completeTaskDenied = permissionDeniedError(
|
|
268
|
+
const completeTaskDenied = permissionDeniedError(targetPath, "complete_task", project);
|
|
264
269
|
if (completeTaskDenied)
|
|
265
270
|
return mcpResponse({ ok: false, error: completeTaskDenied });
|
|
266
271
|
if (Array.isArray(item)) {
|
|
267
272
|
return withWriteQueue(async () => {
|
|
268
273
|
const resolvedItems = item
|
|
269
274
|
.map((match) => {
|
|
270
|
-
const
|
|
271
|
-
return
|
|
275
|
+
const resolvedItem = resolveTaskItem(targetPath, project, match);
|
|
276
|
+
return resolvedItem.ok ? resolvedItem.data : null;
|
|
272
277
|
})
|
|
273
278
|
.filter((task) => task !== null);
|
|
274
|
-
const result = completeTasksBatch(
|
|
279
|
+
const result = completeTasksBatch(targetPath, project, item);
|
|
275
280
|
if (!result.ok)
|
|
276
281
|
return mcpResponse({ ok: false, error: result.error });
|
|
277
282
|
const { completed, errors } = result.data;
|
|
@@ -280,7 +285,7 @@ export function register(server, ctx) {
|
|
|
280
285
|
for (const task of resolvedItems) {
|
|
281
286
|
if (!completedSet.has(task.line))
|
|
282
287
|
continue;
|
|
283
|
-
clearTaskCheckpoint(
|
|
288
|
+
clearTaskCheckpoint(targetPath, {
|
|
284
289
|
project,
|
|
285
290
|
taskId: task.stableId ?? task.id,
|
|
286
291
|
stableId: task.stableId,
|
|
@@ -288,20 +293,20 @@ export function register(server, ctx) {
|
|
|
288
293
|
taskLine: task.line,
|
|
289
294
|
});
|
|
290
295
|
}
|
|
291
|
-
incrementSessionTasksCompleted(
|
|
296
|
+
incrementSessionTasksCompleted(targetPath, completed.length, sessionId, project);
|
|
292
297
|
}
|
|
293
298
|
if (completed.length > 0)
|
|
294
|
-
refreshTaskIndex(updateFileInIndex,
|
|
299
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
295
300
|
return mcpResponse({ ok: completed.length > 0, ...(completed.length === 0 ? { error: `No tasks completed: ${errors.join("; ")}` } : {}), message: `Completed ${completed.length}/${item.length} items`, data: { project, completed, errors } });
|
|
296
301
|
});
|
|
297
302
|
}
|
|
298
303
|
return withWriteQueue(async () => {
|
|
299
|
-
const before = resolveTaskItem(
|
|
300
|
-
const result = completeTaskStore(
|
|
304
|
+
const before = resolveTaskItem(targetPath, project, item);
|
|
305
|
+
const result = completeTaskStore(targetPath, project, item);
|
|
301
306
|
if (!result.ok)
|
|
302
307
|
return mcpResponse({ ok: false, error: result.error });
|
|
303
308
|
if (before.ok) {
|
|
304
|
-
clearTaskCheckpoint(
|
|
309
|
+
clearTaskCheckpoint(targetPath, {
|
|
305
310
|
project,
|
|
306
311
|
taskId: before.data.stableId ?? before.data.id,
|
|
307
312
|
stableId: before.data.stableId,
|
|
@@ -309,8 +314,8 @@ export function register(server, ctx) {
|
|
|
309
314
|
taskLine: before.data.line,
|
|
310
315
|
});
|
|
311
316
|
}
|
|
312
|
-
incrementSessionTasksCompleted(
|
|
313
|
-
refreshTaskIndex(updateFileInIndex,
|
|
317
|
+
incrementSessionTasksCompleted(targetPath, 1, sessionId, project);
|
|
318
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
314
319
|
return mcpResponse({ ok: true, message: result.data, data: { project, item } });
|
|
315
320
|
});
|
|
316
321
|
});
|
|
@@ -324,28 +329,31 @@ export function register(server, ctx) {
|
|
|
324
329
|
z.array(z.string()).describe("List of partial item texts or IDs to remove."),
|
|
325
330
|
]).describe("The task(s) to remove. Pass a string for one, or an array for bulk."),
|
|
326
331
|
}),
|
|
327
|
-
}, async ({ project, item }) => {
|
|
332
|
+
}, async ({ project: projectInput, item }) => {
|
|
333
|
+
const resolved = resolveStoreForProject(ctx, projectInput);
|
|
334
|
+
const project = resolved.project;
|
|
335
|
+
const targetPath = resolved.phrenPath;
|
|
328
336
|
if (!isValidProjectName(project))
|
|
329
337
|
return mcpResponse({ ok: false, error: `Invalid project name: "${project}"` });
|
|
330
|
-
const removeTaskDenied = permissionDeniedError(
|
|
338
|
+
const removeTaskDenied = permissionDeniedError(targetPath, "remove_task", project);
|
|
331
339
|
if (removeTaskDenied)
|
|
332
340
|
return mcpResponse({ ok: false, error: removeTaskDenied });
|
|
333
341
|
if (Array.isArray(item)) {
|
|
334
342
|
return withWriteQueue(async () => {
|
|
335
|
-
const result = removeTasksBatch(
|
|
343
|
+
const result = removeTasksBatch(targetPath, project, item);
|
|
336
344
|
if (!result.ok)
|
|
337
345
|
return mcpResponse({ ok: false, error: result.error });
|
|
338
346
|
const { removed, errors } = result.data;
|
|
339
347
|
if (removed.length > 0)
|
|
340
|
-
refreshTaskIndex(updateFileInIndex,
|
|
348
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
341
349
|
return mcpResponse({ ok: removed.length > 0, ...(removed.length === 0 ? { error: `No tasks removed: ${errors.join("; ")}` } : {}), message: `Removed ${removed.length}/${item.length} items`, data: { project, removed, errors } });
|
|
342
350
|
});
|
|
343
351
|
}
|
|
344
352
|
return withWriteQueue(async () => {
|
|
345
|
-
const result = removeTaskStore(
|
|
353
|
+
const result = removeTaskStore(targetPath, project, item);
|
|
346
354
|
if (!result.ok)
|
|
347
355
|
return mcpResponse({ ok: false, error: result.error });
|
|
348
|
-
refreshTaskIndex(updateFileInIndex,
|
|
356
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
349
357
|
return mcpResponse({ ok: true, message: result.data, data: { project, item } });
|
|
350
358
|
});
|
|
351
359
|
});
|
|
@@ -373,10 +381,13 @@ export function register(server, ctx) {
|
|
|
373
381
|
work_next: z.boolean().optional().describe("If true, pick the highest-priority Queue item and move it to Active. Ignores item param."),
|
|
374
382
|
}).describe("Fields to update. All are optional."),
|
|
375
383
|
}),
|
|
376
|
-
}, async ({ project, item, updates }) => {
|
|
384
|
+
}, async ({ project: projectInput, item, updates }) => {
|
|
385
|
+
const resolved = resolveStoreForProject(ctx, projectInput);
|
|
386
|
+
const project = resolved.project;
|
|
387
|
+
const targetPath = resolved.phrenPath;
|
|
377
388
|
if (!isValidProjectName(project))
|
|
378
389
|
return mcpResponse({ ok: false, error: `Invalid project name: "${project}"` });
|
|
379
|
-
const updateTaskDenied = permissionDeniedError(
|
|
390
|
+
const updateTaskDenied = permissionDeniedError(targetPath, "update_task", project);
|
|
380
391
|
if (updateTaskDenied)
|
|
381
392
|
return mcpResponse({ ok: false, error: updateTaskDenied });
|
|
382
393
|
// Runtime validation: item is required unless work_next is true
|
|
@@ -417,26 +428,26 @@ export function register(server, ctx) {
|
|
|
417
428
|
return withWriteQueue(async () => {
|
|
418
429
|
// Handle work_next: pick highest-priority Queue item, move to Active
|
|
419
430
|
if (updates.work_next) {
|
|
420
|
-
const result = workNextTask(
|
|
431
|
+
const result = workNextTask(targetPath, project);
|
|
421
432
|
if (!result.ok)
|
|
422
433
|
return mcpResponse({ ok: false, error: result.error });
|
|
423
|
-
refreshTaskIndex(updateFileInIndex,
|
|
434
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
424
435
|
return mcpResponse({ ok: true, message: result.data, data: { project } });
|
|
425
436
|
}
|
|
426
437
|
// Handle pin
|
|
427
438
|
if (updates.pin) {
|
|
428
|
-
const result = pinTask(
|
|
439
|
+
const result = pinTask(targetPath, project, item);
|
|
429
440
|
if (!result.ok)
|
|
430
441
|
return mcpResponse({ ok: false, error: result.error });
|
|
431
|
-
refreshTaskIndex(updateFileInIndex,
|
|
442
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
432
443
|
return mcpResponse({ ok: true, message: result.data, data: { project, item } });
|
|
433
444
|
}
|
|
434
445
|
// Handle promote (clear speculative flag)
|
|
435
446
|
if (updates.promote) {
|
|
436
|
-
const result = promoteTask(
|
|
447
|
+
const result = promoteTask(targetPath, project, item, updates.move_to_active ?? false);
|
|
437
448
|
if (!result.ok)
|
|
438
449
|
return mcpResponse({ ok: false, error: result.error });
|
|
439
|
-
refreshTaskIndex(updateFileInIndex,
|
|
450
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
440
451
|
return mcpResponse({
|
|
441
452
|
ok: true,
|
|
442
453
|
message: `Promoted task "${result.data.line}" in ${project}${updates.move_to_active ? " (moved to Active)" : ""}.`,
|
|
@@ -444,10 +455,10 @@ export function register(server, ctx) {
|
|
|
444
455
|
});
|
|
445
456
|
}
|
|
446
457
|
if (updates.create_issue) {
|
|
447
|
-
const
|
|
448
|
-
if (!
|
|
449
|
-
return mcpResponse({ ok: false, error:
|
|
450
|
-
const repo = resolveProjectGithubRepo(
|
|
458
|
+
const resolvedItem = resolveTaskItem(targetPath, project, item);
|
|
459
|
+
if (!resolvedItem.ok)
|
|
460
|
+
return mcpResponse({ ok: false, error: resolvedItem.error });
|
|
461
|
+
const repo = resolveProjectGithubRepo(targetPath, project);
|
|
451
462
|
if (!repo) {
|
|
452
463
|
return mcpResponse({
|
|
453
464
|
ok: false,
|
|
@@ -456,18 +467,18 @@ export function register(server, ctx) {
|
|
|
456
467
|
}
|
|
457
468
|
const created = createGithubIssueForTask({
|
|
458
469
|
repo,
|
|
459
|
-
title:
|
|
460
|
-
body: buildTaskIssueBody(project,
|
|
470
|
+
title: resolvedItem.data.line.replace(/\s*\[(high|medium|low)\]\s*$/i, "").trim(),
|
|
471
|
+
body: buildTaskIssueBody(project, resolvedItem.data),
|
|
461
472
|
});
|
|
462
473
|
if (!created.ok)
|
|
463
474
|
return mcpResponse({ ok: false, error: created.error, errorCode: created.code });
|
|
464
|
-
const linked = linkTaskIssue(
|
|
475
|
+
const linked = linkTaskIssue(targetPath, project, item, {
|
|
465
476
|
github_issue: created.data.issueNumber,
|
|
466
477
|
github_url: created.data.url,
|
|
467
478
|
});
|
|
468
479
|
if (!linked.ok)
|
|
469
480
|
return mcpResponse({ ok: false, error: linked.error, errorCode: linked.code });
|
|
470
|
-
refreshTaskIndex(updateFileInIndex,
|
|
481
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
471
482
|
return mcpResponse({
|
|
472
483
|
ok: true,
|
|
473
484
|
message: `Created GitHub issue ${created.data.issueNumber ? `#${created.data.issueNumber}` : created.data.url} for ${project} task.`,
|
|
@@ -487,14 +498,14 @@ export function register(server, ctx) {
|
|
|
487
498
|
if (updates.unlink_github && (updates.github_issue !== undefined || updates.github_url)) {
|
|
488
499
|
return mcpResponse({ ok: false, error: "Use either unlink_github=true or github_issue/github_url, not both." });
|
|
489
500
|
}
|
|
490
|
-
const result = linkTaskIssue(
|
|
501
|
+
const result = linkTaskIssue(targetPath, project, item, {
|
|
491
502
|
github_issue: updates.github_issue,
|
|
492
503
|
github_url: updates.github_url,
|
|
493
504
|
unlink: updates.unlink_github ?? false,
|
|
494
505
|
});
|
|
495
506
|
if (!result.ok)
|
|
496
507
|
return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
|
|
497
|
-
refreshTaskIndex(updateFileInIndex,
|
|
508
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
498
509
|
return mcpResponse({
|
|
499
510
|
ok: true,
|
|
500
511
|
message: updates.unlink_github
|
|
@@ -510,10 +521,10 @@ export function register(server, ctx) {
|
|
|
510
521
|
});
|
|
511
522
|
}
|
|
512
523
|
// Standard update path
|
|
513
|
-
const result = updateTaskStore(
|
|
524
|
+
const result = updateTaskStore(targetPath, project, item, updates);
|
|
514
525
|
if (!result.ok)
|
|
515
526
|
return mcpResponse({ ok: false, error: result.error });
|
|
516
|
-
refreshTaskIndex(updateFileInIndex,
|
|
527
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
517
528
|
return mcpResponse({ ok: true, message: result.data, data: { project, item, updates } });
|
|
518
529
|
});
|
|
519
530
|
});
|
|
@@ -525,15 +536,18 @@ export function register(server, ctx) {
|
|
|
525
536
|
keep: z.number().optional().describe("Number of recent Done items to keep. Default 30."),
|
|
526
537
|
dry_run: z.boolean().optional().describe("If true, preview changes without writing."),
|
|
527
538
|
}),
|
|
528
|
-
}, async ({ project, keep, dry_run }) => {
|
|
539
|
+
}, async ({ project: projectInput, keep, dry_run }) => {
|
|
540
|
+
const resolved = resolveStoreForProject(ctx, projectInput);
|
|
541
|
+
const project = resolved.project;
|
|
542
|
+
const targetPath = resolved.phrenPath;
|
|
529
543
|
if (!isValidProjectName(project))
|
|
530
544
|
return mcpResponse({ ok: false, error: `Invalid project name: "${project}"` });
|
|
531
545
|
return withWriteQueue(async () => {
|
|
532
|
-
const result = tidyDoneTasks(
|
|
546
|
+
const result = tidyDoneTasks(targetPath, project, keep ?? 30, dry_run ?? false);
|
|
533
547
|
if (!result.ok)
|
|
534
548
|
return mcpResponse({ ok: false, error: result.error });
|
|
535
549
|
if (!dry_run)
|
|
536
|
-
refreshTaskIndex(updateFileInIndex,
|
|
550
|
+
refreshTaskIndex(updateFileInIndex, targetPath, project);
|
|
537
551
|
return mcpResponse({ ok: true, message: result.data, data: { project, keep: keep ?? 30, dryRun: dry_run ?? false } });
|
|
538
552
|
});
|
|
539
553
|
});
|
package/mcp/dist/ui/data.js
CHANGED
|
@@ -151,42 +151,69 @@ export function getHooksData(phrenPath, profile) {
|
|
|
151
151
|
return { globalEnabled, tools, customHooks: readCustomHooks(phrenPath), projectOverrides };
|
|
152
152
|
}
|
|
153
153
|
export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
|
|
154
|
-
|
|
154
|
+
// Collect projects from primary store and all readable non-primary stores
|
|
155
|
+
const storeProjects = [];
|
|
156
|
+
const primaryProjects = getProjectDirs(phrenPath, profile)
|
|
157
|
+
.map((projectDir) => path.basename(projectDir))
|
|
158
|
+
.filter((project) => project !== "global");
|
|
159
|
+
for (const project of primaryProjects)
|
|
160
|
+
storeProjects.push({ storePath: phrenPath, project });
|
|
161
|
+
for (const store of getNonPrimaryStores(phrenPath)) {
|
|
162
|
+
if (!fs.existsSync(store.path))
|
|
163
|
+
continue;
|
|
164
|
+
try {
|
|
165
|
+
const storeProjectDirs = getProjectDirs(store.path)
|
|
166
|
+
.map((projectDir) => path.basename(projectDir))
|
|
167
|
+
.filter((project) => project !== "global");
|
|
168
|
+
for (const project of storeProjectDirs)
|
|
169
|
+
storeProjects.push({ storePath: store.path, project });
|
|
170
|
+
}
|
|
171
|
+
catch { /* store not accessible — skip */ }
|
|
172
|
+
}
|
|
173
|
+
const projects = storeProjects.map((sp) => sp.project);
|
|
155
174
|
const nodes = [];
|
|
156
175
|
const links = [];
|
|
157
176
|
const projectSet = new Set(projects);
|
|
158
177
|
// Collect all unique topics across projects for the UI
|
|
159
178
|
const topicMetaMap = new Map();
|
|
160
|
-
|
|
179
|
+
// Track which project nodes have already been pushed (same name may appear in multiple stores)
|
|
180
|
+
const addedProjectNodeIds = new Set();
|
|
181
|
+
for (const { storePath, project } of storeProjects) {
|
|
161
182
|
// Load dynamic topics for this project
|
|
162
|
-
const { topics: projectTopics } = readProjectTopics(
|
|
183
|
+
const { topics: projectTopics } = readProjectTopics(storePath, project);
|
|
163
184
|
for (const topic of projectTopics) {
|
|
164
185
|
if (!topicMetaMap.has(topic.slug)) {
|
|
165
186
|
topicMetaMap.set(topic.slug, { slug: topic.slug, label: topic.label });
|
|
166
187
|
}
|
|
167
188
|
}
|
|
168
|
-
const findingsPath = path.join(
|
|
189
|
+
const findingsPath = path.join(storePath, project, "FINDINGS.md");
|
|
169
190
|
if (!fs.existsSync(findingsPath)) {
|
|
191
|
+
if (!addedProjectNodeIds.has(project)) {
|
|
192
|
+
addedProjectNodeIds.add(project);
|
|
193
|
+
nodes.push({
|
|
194
|
+
id: project,
|
|
195
|
+
label: project,
|
|
196
|
+
fullLabel: project,
|
|
197
|
+
group: "project",
|
|
198
|
+
refCount: 0,
|
|
199
|
+
project,
|
|
200
|
+
tagged: false,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (!addedProjectNodeIds.has(project)) {
|
|
206
|
+
addedProjectNodeIds.add(project);
|
|
170
207
|
nodes.push({
|
|
171
208
|
id: project,
|
|
172
209
|
label: project,
|
|
173
210
|
fullLabel: project,
|
|
174
211
|
group: "project",
|
|
175
|
-
refCount:
|
|
212
|
+
refCount: 1,
|
|
176
213
|
project,
|
|
177
214
|
tagged: false,
|
|
178
215
|
});
|
|
179
|
-
continue;
|
|
180
216
|
}
|
|
181
|
-
nodes.push({
|
|
182
|
-
id: project,
|
|
183
|
-
label: project,
|
|
184
|
-
fullLabel: project,
|
|
185
|
-
group: "project",
|
|
186
|
-
refCount: 1,
|
|
187
|
-
project,
|
|
188
|
-
tagged: false,
|
|
189
|
-
});
|
|
190
217
|
const content = fs.readFileSync(findingsPath, "utf8");
|
|
191
218
|
const lines = content.split("\n");
|
|
192
219
|
// No cap for focused project; high caps otherwise
|
|
@@ -316,8 +343,8 @@ export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
|
|
|
316
343
|
}
|
|
317
344
|
// ── Tasks ──────────────────────────────────────────────────────────
|
|
318
345
|
try {
|
|
319
|
-
for (const project of
|
|
320
|
-
const taskResult = readTasks(
|
|
346
|
+
for (const { storePath, project } of storeProjects) {
|
|
347
|
+
const taskResult = readTasks(storePath, project);
|
|
321
348
|
if (!taskResult.ok)
|
|
322
349
|
continue;
|
|
323
350
|
const doc = taskResult.data;
|
|
@@ -443,8 +470,8 @@ export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
|
|
|
443
470
|
}
|
|
444
471
|
// ── Reference docs ────────────────────────────────────────────────
|
|
445
472
|
try {
|
|
446
|
-
for (const project of
|
|
447
|
-
const refDir = path.join(
|
|
473
|
+
for (const { storePath, project } of storeProjects) {
|
|
474
|
+
const refDir = path.join(storePath, project, "reference");
|
|
448
475
|
if (!fs.existsSync(refDir) || !fs.statSync(refDir).isDirectory())
|
|
449
476
|
continue;
|
|
450
477
|
const files = fs.readdirSync(refDir);
|
package/mcp/dist/ui/server.js
CHANGED
|
@@ -543,7 +543,8 @@ function handleGetFindings(res, pathname, ctx) {
|
|
|
543
543
|
const project = decodeURIComponent(pathname.slice("/api/findings/".length));
|
|
544
544
|
if (!project || !isValidProjectName(project))
|
|
545
545
|
return jsonErr(res, "Invalid project name", 400);
|
|
546
|
-
const
|
|
546
|
+
const basePath = resolveProjectBasePath(ctx.phrenPath, project);
|
|
547
|
+
const result = readFindings(basePath, project);
|
|
547
548
|
jsonOk(res, result.ok ? { ok: true, data: { project, findings: result.data } } : { ok: false, error: result.error });
|
|
548
549
|
}
|
|
549
550
|
// ── POST handlers ─────────────────────────────────────────────────────────────
|