@phren/cli 0.0.50 → 0.0.53

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.
@@ -2,7 +2,7 @@ import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import { createHash } from "crypto";
4
4
  import { getProjectDirs, runtimeDir, runtimeHealthFile, memoryUsageLogFile, homePath, } from "../shared.js";
5
- import { getNonPrimaryStores } from "../store-registry.js";
5
+ import { getNonPrimaryStores, getStoreProjectDirs } from "../store-registry.js";
6
6
  import { errorMessage } from "../utils.js";
7
7
  import { readInstallPreferences } from "../init/preferences.js";
8
8
  import { readCustomHooks } from "../hooks.js";
@@ -151,42 +151,76 @@ 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
- const projects = getProjectDirs(phrenPath, profile).map((projectDir) => path.basename(projectDir)).filter((project) => project !== "global");
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
+ // Map store paths to store names
162
+ const storePathToName = new Map();
163
+ storePathToName.set(phrenPath, "primary");
164
+ for (const store of getNonPrimaryStores(phrenPath)) {
165
+ if (!fs.existsSync(store.path))
166
+ continue;
167
+ storePathToName.set(store.path, store.name);
168
+ try {
169
+ const storeProjectDirs = getStoreProjectDirs(store)
170
+ .map((projectDir) => path.basename(projectDir))
171
+ .filter((project) => project !== "global");
172
+ for (const project of storeProjectDirs)
173
+ storeProjects.push({ storePath: store.path, project });
174
+ }
175
+ catch { /* store not accessible — skip */ }
176
+ }
177
+ const projects = storeProjects.map((sp) => sp.project);
155
178
  const nodes = [];
156
179
  const links = [];
157
180
  const projectSet = new Set(projects);
158
181
  // Collect all unique topics across projects for the UI
159
182
  const topicMetaMap = new Map();
160
- for (const project of projects) {
183
+ // Track which project nodes have already been pushed (same name may appear in multiple stores)
184
+ const addedProjectNodeIds = new Set();
185
+ for (const { storePath, project } of storeProjects) {
161
186
  // Load dynamic topics for this project
162
- const { topics: projectTopics } = readProjectTopics(phrenPath, project);
187
+ const { topics: projectTopics } = readProjectTopics(storePath, project);
163
188
  for (const topic of projectTopics) {
164
189
  if (!topicMetaMap.has(topic.slug)) {
165
190
  topicMetaMap.set(topic.slug, { slug: topic.slug, label: topic.label });
166
191
  }
167
192
  }
168
- const findingsPath = path.join(phrenPath, project, "FINDINGS.md");
193
+ const findingsPath = path.join(storePath, project, "FINDINGS.md");
194
+ const storeName = storePathToName.get(storePath) || "unknown";
169
195
  if (!fs.existsSync(findingsPath)) {
196
+ if (!addedProjectNodeIds.has(project)) {
197
+ addedProjectNodeIds.add(project);
198
+ nodes.push({
199
+ id: project,
200
+ label: project,
201
+ fullLabel: project,
202
+ group: "project",
203
+ refCount: 0,
204
+ project,
205
+ store: storeName,
206
+ tagged: false,
207
+ });
208
+ }
209
+ continue;
210
+ }
211
+ if (!addedProjectNodeIds.has(project)) {
212
+ addedProjectNodeIds.add(project);
170
213
  nodes.push({
171
214
  id: project,
172
215
  label: project,
173
216
  fullLabel: project,
174
217
  group: "project",
175
- refCount: 0,
218
+ refCount: 1,
176
219
  project,
220
+ store: storeName,
177
221
  tagged: false,
178
222
  });
179
- continue;
180
223
  }
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
224
  const content = fs.readFileSync(findingsPath, "utf8");
191
225
  const lines = content.split("\n");
192
226
  // No cap for focused project; high caps otherwise
@@ -236,6 +270,7 @@ export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
236
270
  group: `topic:${topic.slug}`,
237
271
  refCount: taggedCount,
238
272
  project,
273
+ store: storeName,
239
274
  tagged: true,
240
275
  scoreKey,
241
276
  scoreKeys: [scoreKey],
@@ -270,6 +305,7 @@ export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
270
305
  group: `topic:${topic.slug}`,
271
306
  refCount: taggedCount,
272
307
  project,
308
+ store: storeName,
273
309
  tagged: true,
274
310
  scoreKey,
275
311
  scoreKeys: [scoreKey],
@@ -304,6 +340,7 @@ export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
304
340
  group: `topic:${topic.slug}`,
305
341
  refCount: untaggedAdded,
306
342
  project,
343
+ store: storeName,
307
344
  tagged: false,
308
345
  scoreKey,
309
346
  scoreKeys: [scoreKey],
@@ -316,10 +353,11 @@ export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
316
353
  }
317
354
  // ── Tasks ──────────────────────────────────────────────────────────
318
355
  try {
319
- for (const project of projects) {
320
- const taskResult = readTasks(phrenPath, project);
356
+ for (const { storePath, project } of storeProjects) {
357
+ const taskResult = readTasks(storePath, project);
321
358
  if (!taskResult.ok)
322
359
  continue;
360
+ const taskStoreName = storePathToName.get(storePath) || "unknown";
323
361
  const doc = taskResult.data;
324
362
  let taskCount = 0;
325
363
  const MAX_TASKS = 50;
@@ -337,6 +375,7 @@ export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
337
375
  fullLabel: item.line,
338
376
  group,
339
377
  project,
378
+ store: taskStoreName,
340
379
  tagged: false,
341
380
  scoreKey,
342
381
  scoreKeys: [scoreKey],
@@ -443,8 +482,9 @@ export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
443
482
  }
444
483
  // ── Reference docs ────────────────────────────────────────────────
445
484
  try {
446
- for (const project of projects) {
447
- const refDir = path.join(phrenPath, project, "reference");
485
+ for (const { storePath, project } of storeProjects) {
486
+ const refStoreName = storePathToName.get(storePath) || "unknown";
487
+ const refDir = path.join(storePath, project, "reference");
448
488
  if (!fs.existsSync(refDir) || !fs.statSync(refDir).isDirectory())
449
489
  continue;
450
490
  const files = fs.readdirSync(refDir);
@@ -461,6 +501,7 @@ export async function buildGraph(phrenPath, profile, focusProject, existingDb) {
461
501
  fullLabel: file,
462
502
  group: "reference",
463
503
  project,
504
+ store: refStoreName,
464
505
  tagged: false,
465
506
  scoreKeys: [],
466
507
  refDocs: [{ doc: docRef, project }],
@@ -613,7 +654,7 @@ export function collectProjectsForUI(phrenPath, profile) {
613
654
  for (const store of teamStores) {
614
655
  if (!fs.existsSync(store.path))
615
656
  continue;
616
- const teamProjects = getProjectDirs(store.path).map((d) => path.basename(d)).filter((p) => p !== "global");
657
+ const teamProjects = getStoreProjectDirs(store).map((d) => path.basename(d)).filter((p) => p !== "global");
617
658
  for (const project of teamProjects) {
618
659
  if (seen.has(project))
619
660
  continue; // skip if same name exists in primary
@@ -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 result = readFindings(ctx.phrenPath, project);
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 ─────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phren/cli",
3
- "version": "0.0.50",
3
+ "version": "0.0.53",
4
4
  "description": "Knowledge layer for AI agents. Phren learns and recalls.",
5
5
  "type": "module",
6
6
  "bin": {