@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.
- package/README.md +11 -0
- package/mcp/dist/cli/actions.js +17 -2
- package/mcp/dist/cli/cli.js +1 -1
- package/mcp/dist/cli/namespaces.js +77 -12
- package/mcp/dist/cli/ops.js +2 -2
- package/mcp/dist/content/validate.js +17 -0
- package/mcp/dist/data/access.js +26 -0
- package/mcp/dist/data/tasks.js +27 -2
- package/mcp/dist/generated/memory-ui-graph.browser.js +29 -26
- package/mcp/dist/governance/policy.js +3 -1
- package/mcp/dist/memory-ui-graph.runtime.js +29 -26
- package/mcp/dist/phren-core.js +1 -1
- package/mcp/dist/profile-store.js +19 -0
- package/mcp/dist/project-config.js +26 -0
- package/mcp/dist/shared/index.js +2 -2
- package/mcp/dist/shell/view.js +32 -12
- package/mcp/dist/store-registry.js +41 -0
- 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 +25 -14
- 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/search.js +8 -9
- package/mcp/dist/tools/session.js +58 -18
- package/mcp/dist/tools/tasks.js +58 -44
- package/mcp/dist/ui/data.js +62 -21
- package/mcp/dist/ui/server.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,6 +68,17 @@ phren team join git@github.com:org/phren-team.git
|
|
|
68
68
|
|
|
69
69
|
Each team store syncs independently. Run `phren team list` to see all registered stores.
|
|
70
70
|
|
|
71
|
+
### Filtering Team Store Projects
|
|
72
|
+
|
|
73
|
+
Subscribe to only the projects you care about:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
phren store subscribe qualus-shared arc intranet ogrid
|
|
77
|
+
phren store unsubscribe qualus-shared dendron powergrid-api
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Unsubscribed projects still exist in the store but won't appear in search, UI, or context injection.
|
|
81
|
+
|
|
71
82
|
---
|
|
72
83
|
|
|
73
84
|
MIT License. Made by [Ala Arab](https://github.com/alaarab).
|
package/mcp/dist/cli/actions.js
CHANGED
|
@@ -15,6 +15,19 @@ import { runSearch, runFragmentSearch, parseFragmentSearchArgs, runRelatedDocs,
|
|
|
15
15
|
import { resolveRuntimeProfile } from "../runtime-profile.js";
|
|
16
16
|
import { getProjectConsolidationStatus, CONSOLIDATION_ENTRY_THRESHOLD } from "../content/validate.js";
|
|
17
17
|
import { listAllSessions } from "../tools/session.js";
|
|
18
|
+
function resolveProjectStorePath(phrenPath, project) {
|
|
19
|
+
try {
|
|
20
|
+
const { getNonPrimaryStores } = require("../store-registry.js");
|
|
21
|
+
if (fs.existsSync(path.join(phrenPath, project)))
|
|
22
|
+
return phrenPath;
|
|
23
|
+
for (const store of getNonPrimaryStores(phrenPath)) {
|
|
24
|
+
if (fs.existsSync(path.join(store.path, project)))
|
|
25
|
+
return store.path;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch { /* fall through */ }
|
|
29
|
+
return phrenPath;
|
|
30
|
+
}
|
|
18
31
|
async function runAndPrint(fn) {
|
|
19
32
|
const result = await fn();
|
|
20
33
|
if (result.lines.length > 0)
|
|
@@ -69,7 +82,8 @@ export async function handleTruths(project) {
|
|
|
69
82
|
process.exit(1);
|
|
70
83
|
}
|
|
71
84
|
const phrenPath = getPhrenPath();
|
|
72
|
-
const
|
|
85
|
+
const storePath = resolveProjectStorePath(phrenPath, project);
|
|
86
|
+
const truthsPath = path.join(storePath, project, "truths.md");
|
|
73
87
|
if (!fs.existsSync(truthsPath)) {
|
|
74
88
|
console.log(`No truths pinned for "${project}" yet.`);
|
|
75
89
|
console.log(`\nPin one: phren pin ${project} "your truth here"`);
|
|
@@ -375,7 +389,8 @@ export async function handleConsolidationStatus(args) {
|
|
|
375
389
|
? (() => {
|
|
376
390
|
if (!isValidProjectName(project))
|
|
377
391
|
return null;
|
|
378
|
-
const
|
|
392
|
+
const storePath = resolveProjectStorePath(phrenPath, project);
|
|
393
|
+
const dir = path.join(storePath, project);
|
|
379
394
|
return fs.existsSync(dir) ? [dir] : [];
|
|
380
395
|
})()
|
|
381
396
|
: getProjectDirs(phrenPath, profile);
|
package/mcp/dist/cli/cli.js
CHANGED
|
@@ -79,7 +79,7 @@ export async function runCliCommand(command, args) {
|
|
|
79
79
|
case "tasks":
|
|
80
80
|
return handleTaskView(getProfile());
|
|
81
81
|
case "sessions":
|
|
82
|
-
return handleSessionsView(args);
|
|
82
|
+
return await handleSessionsView(args);
|
|
83
83
|
case "task":
|
|
84
84
|
return handleTaskNamespace(args);
|
|
85
85
|
case "finding":
|
|
@@ -4,6 +4,19 @@ import { execFileSync } from "child_process";
|
|
|
4
4
|
import { expandHomePath, findArchivedProjectNameCaseInsensitive, findProjectNameCaseInsensitive, getPhrenPath, getProjectDirs, homePath, hookConfigPath, normalizeProjectNameForCreate, readRootManifest, } from "../shared.js";
|
|
5
5
|
import { isValidProjectName, errorMessage } from "../utils.js";
|
|
6
6
|
import { logger } from "../logger.js";
|
|
7
|
+
function resolveProjectStorePath(phrenPath, project) {
|
|
8
|
+
try {
|
|
9
|
+
const { getNonPrimaryStores } = require("../store-registry.js");
|
|
10
|
+
if (fs.existsSync(path.join(phrenPath, project)))
|
|
11
|
+
return phrenPath;
|
|
12
|
+
for (const store of getNonPrimaryStores(phrenPath)) {
|
|
13
|
+
if (fs.existsSync(path.join(store.path, project)))
|
|
14
|
+
return store.path;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch { /* fall through */ }
|
|
18
|
+
return phrenPath;
|
|
19
|
+
}
|
|
7
20
|
import { readInstallPreferences, writeInstallPreferences } from "../init/preferences.js";
|
|
8
21
|
import { buildSkillManifest, findLocalSkill, findSkill, getAllSkills } from "../skill/registry.js";
|
|
9
22
|
import { detectSkillCollisions } from "../link/skills.js";
|
|
@@ -252,10 +265,17 @@ export function handleHooksNamespace(args) {
|
|
|
252
265
|
const hooksEnabled = prefs.hooksEnabled !== false;
|
|
253
266
|
const toolPrefs = prefs.hookTools && typeof prefs.hookTools === "object" ? prefs.hookTools : {};
|
|
254
267
|
const project = getOptionValue(args.slice(1), "--project");
|
|
255
|
-
if (project &&
|
|
268
|
+
if (project && !isValidProjectName(project)) {
|
|
256
269
|
console.error(`Project "${project}" not found.`);
|
|
257
270
|
process.exit(1);
|
|
258
271
|
}
|
|
272
|
+
if (project) {
|
|
273
|
+
const storePath = resolveProjectStorePath(phrenPath, project);
|
|
274
|
+
if (!fs.existsSync(path.join(storePath, project))) {
|
|
275
|
+
console.error(`Project "${project}" not found.`);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
259
279
|
const rows = HOOK_TOOLS.map((tool) => ({
|
|
260
280
|
tool,
|
|
261
281
|
hookType: "lifecycle",
|
|
@@ -663,7 +683,8 @@ export async function handleProjectsNamespace(args, profile) {
|
|
|
663
683
|
process.exit(1);
|
|
664
684
|
}
|
|
665
685
|
const phrenPath = getPhrenPath();
|
|
666
|
-
const
|
|
686
|
+
const storePath = resolveProjectStorePath(phrenPath, name);
|
|
687
|
+
const projectDir = path.join(storePath, name);
|
|
667
688
|
if (!fs.existsSync(projectDir)) {
|
|
668
689
|
console.error(`Project "${name}" not found.`);
|
|
669
690
|
process.exit(1);
|
|
@@ -673,16 +694,16 @@ export async function handleProjectsNamespace(args, profile) {
|
|
|
673
694
|
const summaryPath = path.join(projectDir, "summary.md");
|
|
674
695
|
if (fs.existsSync(summaryPath))
|
|
675
696
|
exported.summary = fs.readFileSync(summaryPath, "utf8");
|
|
676
|
-
const learningsResult = readFindings(
|
|
697
|
+
const learningsResult = readFindings(storePath, name);
|
|
677
698
|
if (learningsResult.ok)
|
|
678
699
|
exported.learnings = learningsResult.data;
|
|
679
700
|
const findingsPath = path.join(projectDir, "FINDINGS.md");
|
|
680
701
|
if (fs.existsSync(findingsPath))
|
|
681
702
|
exported.findingsRaw = fs.readFileSync(findingsPath, "utf8");
|
|
682
|
-
const taskResult = readTasks(
|
|
703
|
+
const taskResult = readTasks(storePath, name);
|
|
683
704
|
if (taskResult.ok) {
|
|
684
705
|
exported.task = taskResult.data.items;
|
|
685
|
-
const taskRawPath = resolveTaskFilePath(
|
|
706
|
+
const taskRawPath = resolveTaskFilePath(storePath, name);
|
|
686
707
|
if (taskRawPath && fs.existsSync(taskRawPath))
|
|
687
708
|
exported.taskRaw = fs.readFileSync(taskRawPath, "utf8");
|
|
688
709
|
}
|
|
@@ -799,8 +820,9 @@ export async function handleProjectsNamespace(args, profile) {
|
|
|
799
820
|
const phrenPath = getPhrenPath();
|
|
800
821
|
if (subcommand === "archive") {
|
|
801
822
|
const activeProject = findProjectNameCaseInsensitive(phrenPath, name);
|
|
802
|
-
const
|
|
803
|
-
const
|
|
823
|
+
const storePath = resolveProjectStorePath(phrenPath, activeProject ?? name);
|
|
824
|
+
const projectDir = activeProject ? path.join(storePath, activeProject) : path.join(storePath, name);
|
|
825
|
+
const archiveDir = path.join(storePath, `${activeProject ?? name}.archived`);
|
|
804
826
|
if (!fs.existsSync(projectDir)) {
|
|
805
827
|
console.error(`Project "${name}" not found.`);
|
|
806
828
|
process.exit(1);
|
|
@@ -826,8 +848,9 @@ export async function handleProjectsNamespace(args, profile) {
|
|
|
826
848
|
process.exit(1);
|
|
827
849
|
}
|
|
828
850
|
const archivedProject = findArchivedProjectNameCaseInsensitive(phrenPath, name);
|
|
829
|
-
const
|
|
830
|
-
const
|
|
851
|
+
const storePath = resolveProjectStorePath(phrenPath, archivedProject ?? name);
|
|
852
|
+
const projectDir = path.join(storePath, archivedProject ?? name);
|
|
853
|
+
const archiveDir = path.join(storePath, `${archivedProject ?? name}.archived`);
|
|
831
854
|
if (!fs.existsSync(archiveDir)) {
|
|
832
855
|
const available = fs.readdirSync(phrenPath)
|
|
833
856
|
.filter((e) => e.endsWith(".archived"))
|
|
@@ -1323,8 +1346,10 @@ export async function handleFindingNamespace(args) {
|
|
|
1323
1346
|
console.error("Usage: phren finding list <project>");
|
|
1324
1347
|
process.exit(1);
|
|
1325
1348
|
}
|
|
1349
|
+
const phrenPath = getPhrenPath();
|
|
1326
1350
|
const { readFindings } = await import("../data/access.js");
|
|
1327
|
-
const
|
|
1351
|
+
const storePath = resolveProjectStorePath(phrenPath, project);
|
|
1352
|
+
const result = readFindings(storePath, project);
|
|
1328
1353
|
if (!result.ok) {
|
|
1329
1354
|
console.error(result.error);
|
|
1330
1355
|
process.exit(1);
|
|
@@ -1510,6 +1535,8 @@ function printStoreUsage() {
|
|
|
1510
1535
|
console.log(" phren store remove <name> Remove a store (local only)");
|
|
1511
1536
|
console.log(" phren store sync Pull all stores");
|
|
1512
1537
|
console.log(" phren store activity [--limit N] Recent team findings");
|
|
1538
|
+
console.log(" phren store subscribe <name> <project...> Subscribe store to projects");
|
|
1539
|
+
console.log(" phren store unsubscribe <name> <project...> Unsubscribe store from projects");
|
|
1513
1540
|
}
|
|
1514
1541
|
export async function handleStoreNamespace(args) {
|
|
1515
1542
|
const subcommand = args[0];
|
|
@@ -1644,7 +1671,8 @@ export async function handleStoreNamespace(args) {
|
|
|
1644
1671
|
for (const store of teamStores) {
|
|
1645
1672
|
if (!fs.existsSync(store.path))
|
|
1646
1673
|
continue;
|
|
1647
|
-
const
|
|
1674
|
+
const { getStoreProjectDirs } = await import("../store-registry.js");
|
|
1675
|
+
const projectDirs = getStoreProjectDirs(store);
|
|
1648
1676
|
for (const dir of projectDirs) {
|
|
1649
1677
|
const projectName = path.basename(dir);
|
|
1650
1678
|
const journalEntries = readTeamJournalEntries(store.path, projectName);
|
|
@@ -1703,6 +1731,42 @@ export async function handleStoreNamespace(args) {
|
|
|
1703
1731
|
}
|
|
1704
1732
|
return;
|
|
1705
1733
|
}
|
|
1734
|
+
if (subcommand === "subscribe") {
|
|
1735
|
+
const storeName = args[1];
|
|
1736
|
+
const projects = args.slice(2);
|
|
1737
|
+
if (!storeName || projects.length === 0) {
|
|
1738
|
+
console.error("Usage: phren store subscribe <store-name> <project1> [project2...]");
|
|
1739
|
+
process.exit(1);
|
|
1740
|
+
}
|
|
1741
|
+
try {
|
|
1742
|
+
const { subscribeStoreProjects } = await import("../store-registry.js");
|
|
1743
|
+
subscribeStoreProjects(phrenPath, storeName, projects);
|
|
1744
|
+
console.log(`Added ${projects.length} project(s) to "${storeName}"`);
|
|
1745
|
+
}
|
|
1746
|
+
catch (err) {
|
|
1747
|
+
console.error(`Failed to subscribe: ${errorMessage(err)}`);
|
|
1748
|
+
process.exit(1);
|
|
1749
|
+
}
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
if (subcommand === "unsubscribe") {
|
|
1753
|
+
const storeName = args[1];
|
|
1754
|
+
const projects = args.slice(2);
|
|
1755
|
+
if (!storeName || projects.length === 0) {
|
|
1756
|
+
console.error("Usage: phren store unsubscribe <store-name> <project1> [project2...]");
|
|
1757
|
+
process.exit(1);
|
|
1758
|
+
}
|
|
1759
|
+
try {
|
|
1760
|
+
const { unsubscribeStoreProjects } = await import("../store-registry.js");
|
|
1761
|
+
unsubscribeStoreProjects(phrenPath, storeName, projects);
|
|
1762
|
+
console.log(`Removed ${projects.length} project(s) from "${storeName}"`);
|
|
1763
|
+
}
|
|
1764
|
+
catch (err) {
|
|
1765
|
+
console.error(`Failed to unsubscribe: ${errorMessage(err)}`);
|
|
1766
|
+
process.exit(1);
|
|
1767
|
+
}
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1706
1770
|
console.error(`Unknown store subcommand: ${subcommand}`);
|
|
1707
1771
|
printStoreUsage();
|
|
1708
1772
|
process.exit(1);
|
|
@@ -1776,7 +1840,8 @@ function countStoreProjects(store) {
|
|
|
1776
1840
|
if (!fs.existsSync(store.path))
|
|
1777
1841
|
return 0;
|
|
1778
1842
|
try {
|
|
1779
|
-
|
|
1843
|
+
const storeRegistry = require("../store-registry.js");
|
|
1844
|
+
return storeRegistry.getStoreProjectDirs(store).length;
|
|
1780
1845
|
}
|
|
1781
1846
|
catch {
|
|
1782
1847
|
return 0;
|
package/mcp/dist/cli/ops.js
CHANGED
|
@@ -49,7 +49,7 @@ export function handleTaskView(profile) {
|
|
|
49
49
|
}
|
|
50
50
|
console.log(`\n${totalActive} active, ${totalQueue} queued across ${docs.length} project(s).`);
|
|
51
51
|
}
|
|
52
|
-
export function handleSessionsView(args) {
|
|
52
|
+
export async function handleSessionsView(args) {
|
|
53
53
|
const phrenPath = getPhrenPath();
|
|
54
54
|
const sessionId = args[0];
|
|
55
55
|
if (sessionId) {
|
|
@@ -60,7 +60,7 @@ export function handleSessionsView(args) {
|
|
|
60
60
|
console.error(`Session "${sessionId}" not found.`);
|
|
61
61
|
process.exit(1);
|
|
62
62
|
}
|
|
63
|
-
const artifacts = getSessionArtifacts(phrenPath, session.sessionId);
|
|
63
|
+
const artifacts = await getSessionArtifacts(phrenPath, session.sessionId);
|
|
64
64
|
console.log(`Session: ${session.sessionId.slice(0, 8)}`);
|
|
65
65
|
console.log(`Project: ${session.project ?? "—"}`);
|
|
66
66
|
console.log(`Started: ${session.startedAt.slice(0, 16).replace("T", " ")}`);
|
|
@@ -7,6 +7,7 @@ import { errorMessage } from "../utils.js";
|
|
|
7
7
|
import { countActiveFindings } from "./archive.js";
|
|
8
8
|
import { isTaskFileName } from "../data/tasks.js";
|
|
9
9
|
import { METADATA_REGEX } from "./metadata.js";
|
|
10
|
+
import { getNonPrimaryStores, getStoreProjectDirs } from "../store-registry.js";
|
|
10
11
|
/** Maximum allowed length for a single finding entry (token budget protection). */
|
|
11
12
|
export const MAX_FINDING_LENGTH = 2000;
|
|
12
13
|
function safeParseDate(s) {
|
|
@@ -74,6 +75,22 @@ export function checkConsolidationNeeded(phrenPath, profile) {
|
|
|
74
75
|
results.push(status);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
78
|
+
// Include projects from team stores
|
|
79
|
+
try {
|
|
80
|
+
for (const store of getNonPrimaryStores(phrenPath)) {
|
|
81
|
+
if (!fs.existsSync(store.path))
|
|
82
|
+
continue;
|
|
83
|
+
for (const dir of getStoreProjectDirs(store)) {
|
|
84
|
+
const status = getProjectConsolidationStatus(dir);
|
|
85
|
+
if (status && status.recommended) {
|
|
86
|
+
results.push(status);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// store-registry not available or error loading, continue with primary only
|
|
93
|
+
}
|
|
77
94
|
return results;
|
|
78
95
|
}
|
|
79
96
|
/**
|
package/mcp/dist/data/access.js
CHANGED
|
@@ -12,6 +12,7 @@ import { parseCitationComment, parseSourceComment, } from "../content/citation.j
|
|
|
12
12
|
import { parseFindingLifecycle, } from "../finding/lifecycle.js";
|
|
13
13
|
import { METADATA_REGEX, isCitationLine, isArchiveStart, isArchiveEnd, parseFindingId, parseAllContradictions, stripComments, normalizeFindingText, } from "../content/metadata.js";
|
|
14
14
|
import { withSafeLock, ensureProject } from "../shared/data-utils.js";
|
|
15
|
+
import { getNonPrimaryStores, getStoreProjectDirs } from "../store-registry.js";
|
|
15
16
|
export { readTasks, readTasksAcrossProjects, resolveTaskItem, addTask, addTasks, completeTasks, completeTask, removeTask, removeTasks, updateTask, linkTaskIssue, pinTask, unpinTask, workNextTask, tidyDoneTasks, taskMarkdown, appendChildFinding, promoteTask, TASKS_FILENAME, TASK_FILE_ALIASES, canonicalTaskFilePath, resolveTaskFilePath, isTaskFileName, } from "./tasks.js";
|
|
16
17
|
export { addProjectToProfile, listMachines, listProfiles, listProjectCards, removeProjectFromProfile, setMachineProfile, } from "../profile-store.js";
|
|
17
18
|
export { loadShellState, resetShellState, saveShellState, } from "../shell/state-store.js";
|
|
@@ -559,6 +560,7 @@ export function readReviewQueueAcrossProjects(phrenPath, profile) {
|
|
|
559
560
|
Conflicts: 2,
|
|
560
561
|
};
|
|
561
562
|
const items = [];
|
|
563
|
+
const seen = new Set(projects);
|
|
562
564
|
for (const project of projects) {
|
|
563
565
|
const result = readReviewQueue(phrenPath, project);
|
|
564
566
|
if (!result.ok)
|
|
@@ -567,6 +569,30 @@ export function readReviewQueueAcrossProjects(phrenPath, profile) {
|
|
|
567
569
|
items.push({ project, ...item });
|
|
568
570
|
}
|
|
569
571
|
}
|
|
572
|
+
// Include projects from team stores
|
|
573
|
+
try {
|
|
574
|
+
for (const store of getNonPrimaryStores(phrenPath)) {
|
|
575
|
+
if (!fs.existsSync(store.path))
|
|
576
|
+
continue;
|
|
577
|
+
const storeDirs = getStoreProjectDirs(store)
|
|
578
|
+
.map((d) => path.basename(d))
|
|
579
|
+
.filter((p) => p !== "global");
|
|
580
|
+
for (const storeProject of storeDirs) {
|
|
581
|
+
if (seen.has(storeProject))
|
|
582
|
+
continue;
|
|
583
|
+
seen.add(storeProject);
|
|
584
|
+
const result = readReviewQueue(store.path, storeProject);
|
|
585
|
+
if (!result.ok)
|
|
586
|
+
continue;
|
|
587
|
+
for (const item of result.data) {
|
|
588
|
+
items.push({ project: storeProject, ...item });
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
catch {
|
|
594
|
+
// store-registry not available or error loading, continue with primary only
|
|
595
|
+
}
|
|
570
596
|
items.sort((a, b) => {
|
|
571
597
|
const aDate = a.date === "unknown" ? "" : a.date;
|
|
572
598
|
const bDate = b.date === "unknown" ? "" : b.date;
|
package/mcp/dist/data/tasks.js
CHANGED
|
@@ -5,6 +5,7 @@ import { phrenErr, PhrenError, phrenOk, forwardErr, getProjectDirs, } from "../s
|
|
|
5
5
|
import { validateTaskFormat } from "../shared/content.js";
|
|
6
6
|
import { safeProjectPath } from "../utils.js";
|
|
7
7
|
import { withSafeLock, ensureProject } from "../shared/data-utils.js";
|
|
8
|
+
import { getNonPrimaryStores, getStoreProjectDirs } from "../store-registry.js";
|
|
8
9
|
const ACTIVE_HEADINGS = new Set(["active", "in progress", "in-progress", "current", "wip"]);
|
|
9
10
|
const QUEUE_HEADINGS = new Set(["queue", "queued", "task", "todo", "upcoming", "next"]);
|
|
10
11
|
const DONE_HEADINGS = new Set(["done", "completed", "finished", "archived"]);
|
|
@@ -352,9 +353,11 @@ export function readTasks(phrenPath, project) {
|
|
|
352
353
|
return phrenOk(parseTaskContent(project, taskPath, content));
|
|
353
354
|
}
|
|
354
355
|
export function readTasksAcrossProjects(phrenPath, profile) {
|
|
355
|
-
const projects = getProjectDirs(phrenPath, profile).map((dir) => path.basename(dir)).sort();
|
|
356
356
|
const result = [];
|
|
357
|
-
|
|
357
|
+
const seen = new Set();
|
|
358
|
+
// Primary store projects (with profile filtering)
|
|
359
|
+
const primaryProjects = getProjectDirs(phrenPath, profile).map((dir) => path.basename(dir)).sort();
|
|
360
|
+
for (const project of primaryProjects) {
|
|
358
361
|
const file = canonicalTaskFilePath(phrenPath, project);
|
|
359
362
|
if (!file || !fs.existsSync(file))
|
|
360
363
|
continue;
|
|
@@ -362,7 +365,29 @@ export function readTasksAcrossProjects(phrenPath, profile) {
|
|
|
362
365
|
if (!parsed.ok)
|
|
363
366
|
continue;
|
|
364
367
|
result.push(parsed.data);
|
|
368
|
+
seen.add(project);
|
|
365
369
|
}
|
|
370
|
+
// Non-primary store projects (no profile — team stores don't have profiles)
|
|
371
|
+
try {
|
|
372
|
+
for (const store of getNonPrimaryStores(phrenPath)) {
|
|
373
|
+
if (!fs.existsSync(store.path))
|
|
374
|
+
continue;
|
|
375
|
+
const storeProjects = getStoreProjectDirs(store).map((dir) => path.basename(dir));
|
|
376
|
+
for (const project of storeProjects) {
|
|
377
|
+
if (seen.has(project))
|
|
378
|
+
continue;
|
|
379
|
+
seen.add(project);
|
|
380
|
+
const file = canonicalTaskFilePath(store.path, project);
|
|
381
|
+
if (!file || !fs.existsSync(file))
|
|
382
|
+
continue;
|
|
383
|
+
const parsed = readTasks(store.path, project);
|
|
384
|
+
if (!parsed.ok)
|
|
385
|
+
continue;
|
|
386
|
+
result.push(parsed.data);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch { /* store-registry not available — primary only */ }
|
|
366
391
|
return result;
|
|
367
392
|
}
|
|
368
393
|
export function resolveTaskItem(phrenPath, project, match) {
|