@phren/cli 0.0.28 → 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.
Files changed (153) hide show
  1. package/mcp/dist/capabilities/cli.js +2 -5
  2. package/mcp/dist/capabilities/mcp.js +5 -8
  3. package/mcp/dist/capabilities/types.js +2 -5
  4. package/mcp/dist/capabilities/vscode.js +2 -5
  5. package/mcp/dist/capabilities/web-ui.js +2 -5
  6. package/mcp/dist/{cli-actions.js → cli/actions.js} +25 -21
  7. package/mcp/dist/{cli.js → cli/cli.js} +13 -13
  8. package/mcp/dist/{cli-config.js → cli/config.js} +12 -12
  9. package/mcp/dist/{cli-extract.js → cli/extract.js} +8 -8
  10. package/mcp/dist/{cli-govern.js → cli/govern.js} +28 -17
  11. package/mcp/dist/{cli-graph.js → cli/graph.js} +10 -9
  12. package/mcp/dist/{cli-hooks-citations.js → cli/hooks-citations.js} +2 -2
  13. package/mcp/dist/{cli-hooks-context.js → cli/hooks-context.js} +23 -23
  14. package/mcp/dist/{cli-hooks-globs.js → cli/hooks-globs.js} +4 -4
  15. package/mcp/dist/{cli-hooks-output.js → cli/hooks-output.js} +9 -10
  16. package/mcp/dist/{cli-hooks-session.js → cli/hooks-session.js} +58 -117
  17. package/mcp/dist/{cli-hooks.js → cli/hooks.js} +27 -26
  18. package/mcp/dist/{cli-namespaces.js → cli/namespaces.js} +25 -24
  19. package/mcp/dist/{cli-ops.js → cli/ops.js} +9 -9
  20. package/mcp/dist/{cli-search.js → cli/search.js} +12 -11
  21. package/mcp/dist/cli-hooks-git.js +243 -0
  22. package/mcp/dist/cli-hooks-prompt.js +323 -0
  23. package/mcp/dist/cli-hooks-session-handlers.js +337 -0
  24. package/mcp/dist/cli-hooks-stop.js +519 -0
  25. package/mcp/dist/{content-archive.js → content/archive.js} +16 -29
  26. package/mcp/dist/{content-citation.js → content/citation.js} +5 -5
  27. package/mcp/dist/{content-dedup.js → content/dedup.js} +9 -12
  28. package/mcp/dist/{content-learning.js → content/learning.js} +41 -20
  29. package/mcp/dist/{content-validate.js → content/validate.js} +5 -5
  30. package/mcp/dist/{core-finding.js → core/finding.js} +4 -4
  31. package/mcp/dist/{core-project.js → core/project.js} +4 -4
  32. package/mcp/dist/{core-search.js → core/search.js} +2 -2
  33. package/mcp/dist/{data-access.js → data/access.js} +142 -15
  34. package/mcp/dist/{data-tasks.js → data/tasks.js} +7 -5
  35. package/mcp/dist/embedding.js +9 -14
  36. package/mcp/dist/entrypoint.js +11 -11
  37. package/mcp/dist/{finding-context.js → finding/context.js} +2 -2
  38. package/mcp/dist/{finding-impact.js → finding/impact.js} +3 -3
  39. package/mcp/dist/{finding-journal.js → finding/journal.js} +4 -4
  40. package/mcp/dist/{finding-lifecycle.js → finding/lifecycle.js} +13 -7
  41. package/mcp/dist/governance/audit.js +30 -0
  42. package/mcp/dist/{governance-locks.js → governance/locks.js} +14 -9
  43. package/mcp/dist/{governance-policy.js → governance/policy.js} +23 -12
  44. package/mcp/dist/{governance-rbac.js → governance/rbac.js} +4 -4
  45. package/mcp/dist/{governance-scores.js → governance/scores.js} +10 -11
  46. package/mcp/dist/hooks.js +53 -37
  47. package/mcp/dist/index-query.js +4 -1
  48. package/mcp/dist/index.js +54 -30
  49. package/mcp/dist/{init-config.js → init/config.js} +6 -6
  50. package/mcp/dist/{init.js → init/init.js} +80 -69
  51. package/mcp/dist/{init-preferences.js → init/preferences.js} +3 -3
  52. package/mcp/dist/{init-setup.js → init/setup.js} +17 -19
  53. package/mcp/dist/{init-shared.js → init/shared.js} +4 -4
  54. package/mcp/dist/init-bootstrap.js +21 -0
  55. package/mcp/dist/init-detect.js +38 -0
  56. package/mcp/dist/init-env.js +114 -0
  57. package/mcp/dist/init-fresh.js +234 -0
  58. package/mcp/dist/init-hooks.js +26 -0
  59. package/mcp/dist/init-mcp.js +65 -0
  60. package/mcp/dist/init-modes.js +135 -0
  61. package/mcp/dist/init-npm.js +37 -0
  62. package/mcp/dist/init-project-local.js +99 -0
  63. package/mcp/dist/init-semantic.js +48 -0
  64. package/mcp/dist/init-types.js +1 -0
  65. package/mcp/dist/init-uninstall.js +504 -0
  66. package/mcp/dist/init-update.js +96 -0
  67. package/mcp/dist/init-walkthrough.js +524 -0
  68. package/mcp/dist/{link-checksums.js → link/checksums.js} +5 -5
  69. package/mcp/dist/{link-context.js → link/context.js} +4 -4
  70. package/mcp/dist/{link-doctor.js → link/doctor.js} +20 -22
  71. package/mcp/dist/{link.js → link/link.js} +26 -31
  72. package/mcp/dist/{link-skills.js → link/skills.js} +10 -10
  73. package/mcp/dist/logger.js +11 -3
  74. package/mcp/dist/package-metadata.js +1 -1
  75. package/mcp/dist/phren-art.js +4 -126
  76. package/mcp/dist/phren-paths.js +30 -12
  77. package/mcp/dist/proactivity.js +3 -3
  78. package/mcp/dist/profile-store.js +5 -6
  79. package/mcp/dist/project-config.js +2 -2
  80. package/mcp/dist/project-topics.js +17 -47
  81. package/mcp/dist/provider-adapters.js +1 -1
  82. package/mcp/dist/query-correlation.js +1 -1
  83. package/mcp/dist/runtime-profile.js +1 -1
  84. package/mcp/dist/{session-checkpoints.js → session/checkpoints.js} +3 -3
  85. package/mcp/dist/{session-utils.js → session/utils.js} +1 -1
  86. package/mcp/dist/{shared-content.js → shared/content.js} +7 -7
  87. package/mcp/dist/{shared-data-utils.js → shared/data-utils.js} +28 -3
  88. package/mcp/dist/{shared-embedding-cache.js → shared/embedding-cache.js} +3 -3
  89. package/mcp/dist/{shared-fragment-graph.js → shared/fragment-graph.js} +19 -42
  90. package/mcp/dist/shared/governance.js +4 -0
  91. package/mcp/dist/{shared-index.js → shared/index.js} +105 -132
  92. package/mcp/dist/{shared-ollama.js → shared/ollama.js} +25 -7
  93. package/mcp/dist/shared/process.js +24 -0
  94. package/mcp/dist/{shared-retrieval.js → shared/retrieval.js} +22 -24
  95. package/mcp/dist/{shared-search-fallback.js → shared/search-fallback.js} +18 -20
  96. package/mcp/dist/{shared-sqljs.js → shared/sqljs.js} +3 -3
  97. package/mcp/dist/{shared-vector-index.js → shared/vector-index.js} +3 -3
  98. package/mcp/dist/shared.js +6 -60
  99. package/mcp/dist/{shell-entry.js → shell/entry.js} +6 -6
  100. package/mcp/dist/{shell-input.js → shell/input.js} +13 -13
  101. package/mcp/dist/{shell-palette.js → shell/palette.js} +3 -3
  102. package/mcp/dist/{shell-render.js → shell/render.js} +2 -2
  103. package/mcp/dist/{shell.js → shell/shell.js} +11 -11
  104. package/mcp/dist/{shell-state-store.js → shell/state-store.js} +5 -5
  105. package/mcp/dist/{shell-view-list.js → shell/view-list.js} +1 -1
  106. package/mcp/dist/{shell-view.js → shell/view.js} +13 -13
  107. package/mcp/dist/{skill-files.js → skill/files.js} +9 -9
  108. package/mcp/dist/{skill-registry.js → skill/registry.js} +5 -5
  109. package/mcp/dist/{skill-state.js → skill/state.js} +1 -4
  110. package/mcp/dist/startup-embedding.js +2 -2
  111. package/mcp/dist/status.js +15 -14
  112. package/mcp/dist/{tasks-github.js → task/github.js} +3 -2
  113. package/mcp/dist/{task-hygiene.js → task/hygiene.js} +4 -4
  114. package/mcp/dist/{task-lifecycle.js → task/lifecycle.js} +8 -13
  115. package/mcp/dist/telemetry.js +3 -4
  116. package/mcp/dist/tool-registry.js +29 -17
  117. package/mcp/dist/tools/config.js +530 -0
  118. package/mcp/dist/{mcp-data.js → tools/data.js} +8 -10
  119. package/mcp/dist/{mcp-extract-facts.js → tools/extract-facts.js} +6 -6
  120. package/mcp/dist/{mcp-extract.js → tools/extract.js} +6 -6
  121. package/mcp/dist/tools/finding.js +584 -0
  122. package/mcp/dist/{mcp-graph.js → tools/graph.js} +11 -14
  123. package/mcp/dist/{mcp-hooks.js → tools/hooks.js} +6 -6
  124. package/mcp/dist/{mcp-memory.js → tools/memory.js} +5 -5
  125. package/mcp/dist/tools/ops.js +468 -0
  126. package/mcp/dist/tools/search.js +672 -0
  127. package/mcp/dist/{mcp-session.js → tools/session.js} +51 -25
  128. package/mcp/dist/{mcp-skills.js → tools/skills.js} +42 -35
  129. package/mcp/dist/{mcp-tasks.js → tools/tasks.js} +155 -282
  130. package/mcp/dist/{memory-ui-data.js → ui/data.js} +31 -17
  131. package/mcp/dist/{memory-ui.js → ui/memory-ui.js} +3 -3
  132. package/mcp/dist/{memory-ui-page.js → ui/page.js} +5 -7
  133. package/mcp/dist/ui/server.js +1024 -0
  134. package/mcp/dist/update.js +2 -2
  135. package/mcp/dist/utils.js +63 -19
  136. package/package.json +2 -2
  137. package/scripts/preuninstall.mjs +31 -0
  138. package/starter/global/CLAUDE.md +3 -2
  139. package/mcp/dist/governance-audit.js +0 -22
  140. package/mcp/dist/mcp-config.js +0 -551
  141. package/mcp/dist/mcp-finding.js +0 -594
  142. package/mcp/dist/mcp-ops.js +0 -363
  143. package/mcp/dist/mcp-search.js +0 -668
  144. package/mcp/dist/memory-ui-server.js +0 -1411
  145. package/mcp/dist/shared-governance.js +0 -4
  146. /package/mcp/dist/{content-metadata.js → content/metadata.js} +0 -0
  147. /package/mcp/dist/{shared-stemmer.js → shared/stemmer.js} +0 -0
  148. /package/mcp/dist/{shell-types.js → shell/types.js} +0 -0
  149. /package/mcp/dist/{mcp-types.js → tools/types.js} +0 -0
  150. /package/mcp/dist/{memory-ui-assets.js → ui/assets.js} +0 -0
  151. /package/mcp/dist/{memory-ui-graph.js → ui/graph.js} +0 -0
  152. /package/mcp/dist/{memory-ui-scripts.js → ui/scripts.js} +0 -0
  153. /package/mcp/dist/{memory-ui-styles.js → ui/styles.js} +0 -0
@@ -1,363 +0,0 @@
1
- import { mcpResponse } from "./mcp-types.js";
2
- import { z } from "zod";
3
- import * as fs from "fs";
4
- import * as path from "path";
5
- import { runtimeFile, getProjectDirs } from "./shared.js";
6
- import { findFtsCacheForPath } from "./shared-index.js";
7
- import { isValidProjectName, errorMessage } from "./utils.js";
8
- import { readReviewQueue, readReviewQueueAcrossProjects } from "./data-access.js";
9
- import { addProjectFromPath } from "./core-project.js";
10
- import { PROJECT_OWNERSHIP_MODES, parseProjectOwnershipMode } from "./project-config.js";
11
- import { resolveRuntimeProfile } from "./runtime-profile.js";
12
- import { getMachineName } from "./machine-identity.js";
13
- import { getProjectConsolidationStatus, CONSOLIDATION_ENTRY_THRESHOLD } from "./content-validate.js";
14
- export function register(server, ctx) {
15
- const { phrenPath, profile, withWriteQueue } = ctx;
16
- // ── add_project ────────────────────────────────────────────────────────────
17
- server.registerTool("add_project", {
18
- title: "◆ phren · add project",
19
- description: "Bootstrap a project into phren from a repo or working directory. " +
20
- "Copies or creates CLAUDE.md/summary/tasks/findings under ~/.phren/<project> and adds the project to the active profile.",
21
- inputSchema: z.object({
22
- path: z.string().describe("Project path to import. Pass the current repo path explicitly."),
23
- profile: z.string().optional().describe("Profile to update. Defaults to the active profile."),
24
- ownership: z.enum(PROJECT_OWNERSHIP_MODES).optional()
25
- .describe("How Phren should treat repo-facing instruction files: phren-managed, detached, or repo-managed."),
26
- }),
27
- }, async ({ path: targetPath, profile: requestedProfile, ownership }) => {
28
- return withWriteQueue(async () => {
29
- try {
30
- const added = addProjectFromPath(phrenPath, targetPath, requestedProfile || profile || undefined, parseProjectOwnershipMode(ownership) ?? undefined);
31
- if (!added.ok) {
32
- return mcpResponse({
33
- ok: false,
34
- error: added.error,
35
- });
36
- }
37
- await ctx.rebuildIndex();
38
- return mcpResponse({
39
- ok: true,
40
- message: `Added project "${added.data.project}" (${added.data.ownership}) from ${added.data.path}.`,
41
- data: added.data,
42
- });
43
- }
44
- catch (err) {
45
- return mcpResponse({
46
- ok: false,
47
- error: errorMessage(err),
48
- });
49
- }
50
- });
51
- });
52
- server.registerTool("get_consolidation_status", {
53
- title: "◆ phren · consolidation status",
54
- description: "Check whether a project's FINDINGS.md needs consolidation. " +
55
- "Returns entry count since last consolidation, threshold, and recommendation.",
56
- inputSchema: z.object({
57
- project: z.string().optional().describe("Project name. If omitted, checks all projects."),
58
- }),
59
- }, async ({ project }) => {
60
- const projectDirs = project
61
- ? (() => {
62
- if (!isValidProjectName(project))
63
- return [];
64
- const dir = path.join(phrenPath, project);
65
- return fs.existsSync(dir) ? [dir] : [];
66
- })()
67
- : getProjectDirs(phrenPath, profile);
68
- if (project && projectDirs.length === 0) {
69
- return mcpResponse({ ok: false, error: `Project "${project}" not found.` });
70
- }
71
- const results = [];
72
- for (const dir of projectDirs) {
73
- const status = getProjectConsolidationStatus(dir);
74
- if (!status)
75
- continue;
76
- results.push({ ...status, threshold: CONSOLIDATION_ENTRY_THRESHOLD });
77
- }
78
- if (results.length === 0) {
79
- return mcpResponse({ ok: true, message: "No FINDINGS.md files found.", data: { results: [] } });
80
- }
81
- const lines = results.map(r => `${r.project}: ${r.entriesSince} entries since${r.lastConsolidated ? ` ${r.lastConsolidated}` : " (never consolidated)"}` +
82
- `${r.recommended ? " — consolidation recommended" : ""}`);
83
- return mcpResponse({
84
- ok: true,
85
- message: lines.join("\n"),
86
- data: { results },
87
- });
88
- });
89
- // ── health_check ───────────────────────────────────────────────────────────
90
- server.registerTool("health_check", {
91
- title: "◆ phren · health",
92
- description: "Return phren health status: version, FTS index status, hook registration, and profile/machine info.",
93
- inputSchema: z.object({}),
94
- }, async () => {
95
- const activeProfile = (() => {
96
- try {
97
- return resolveRuntimeProfile(phrenPath);
98
- }
99
- catch {
100
- return profile || "";
101
- }
102
- })();
103
- // Version
104
- let version = "unknown";
105
- try {
106
- const pkgPath = path.join(path.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
107
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
108
- version = pkg.version || "unknown";
109
- }
110
- catch (err) {
111
- if ((process.env.PHREN_DEBUG))
112
- process.stderr.write(`[phren] healthCheck version: ${errorMessage(err)}\n`);
113
- }
114
- // FTS index (lives in /tmpphren-fts-*/, not .runtime/)
115
- let indexStatus = { exists: false };
116
- try {
117
- indexStatus = findFtsCacheForPath(phrenPath, activeProfile);
118
- }
119
- catch (err) {
120
- if ((process.env.PHREN_DEBUG))
121
- process.stderr.write(`[phren] healthCheck ftsCacheCheck: ${errorMessage(err)}\n`);
122
- }
123
- // Hook registration
124
- let hooksEnabled = false;
125
- try {
126
- const { getHooksEnabledPreference } = await import("./init-preferences.js");
127
- hooksEnabled = getHooksEnabledPreference(phrenPath);
128
- }
129
- catch (err) {
130
- if ((process.env.PHREN_DEBUG))
131
- process.stderr.write(`[phren] healthCheck hooksEnabled: ${errorMessage(err)}\n`);
132
- }
133
- let mcpEnabled = false;
134
- try {
135
- const { getMcpEnabledPreference } = await import("./init-preferences.js");
136
- mcpEnabled = getMcpEnabledPreference(phrenPath);
137
- }
138
- catch (err) {
139
- if ((process.env.PHREN_DEBUG))
140
- process.stderr.write(`[phren] healthCheck mcpEnabled: ${errorMessage(err)}\n`);
141
- }
142
- // Profile/machine info
143
- const machineName = (() => {
144
- try {
145
- return getMachineName();
146
- }
147
- catch (err) {
148
- if ((process.env.PHREN_DEBUG))
149
- process.stderr.write(`[phren] healthCheck machineName: ${errorMessage(err)}\n`);
150
- }
151
- return undefined;
152
- })();
153
- const projectCount = getProjectDirs(phrenPath, activeProfile).length;
154
- // Proactivity and taskMode
155
- let proactivity = "high";
156
- let taskMode = "auto";
157
- try {
158
- const { getWorkflowPolicy } = await import("./governance-policy.js");
159
- const workflowPolicy = getWorkflowPolicy(phrenPath);
160
- taskMode = workflowPolicy.taskMode;
161
- }
162
- catch (err) {
163
- if ((process.env.PHREN_DEBUG))
164
- process.stderr.write(`[phren] healthCheck taskMode: ${errorMessage(err)}\n`);
165
- }
166
- let syncIntent;
167
- try {
168
- const { readInstallPreferences } = await import("./init-preferences.js");
169
- const prefs = readInstallPreferences(phrenPath);
170
- proactivity = prefs.proactivity || "high";
171
- syncIntent = prefs.syncIntent;
172
- }
173
- catch (err) {
174
- if ((process.env.PHREN_DEBUG))
175
- process.stderr.write(`[phren] healthCheck proactivity: ${errorMessage(err)}\n`);
176
- }
177
- // Determine sync status from intent + git remote state
178
- let syncStatus = "local-only";
179
- let syncDetail = "no git remote configured";
180
- try {
181
- const { execFileSync } = await import("child_process");
182
- const remote = execFileSync("git", ["-C", phrenPath, "remote", "get-url", "origin"], {
183
- encoding: "utf8",
184
- stdio: ["ignore", "pipe", "ignore"],
185
- timeout: 5_000,
186
- }).trim();
187
- if (remote) {
188
- try {
189
- execFileSync("git", ["-C", phrenPath, "ls-remote", "--exit-code", "origin"], {
190
- stdio: ["ignore", "ignore", "ignore"],
191
- timeout: 10_000,
192
- });
193
- syncStatus = "synced";
194
- syncDetail = `origin=${remote}`;
195
- }
196
- catch {
197
- syncStatus = syncIntent === "sync" ? "broken" : "local-only";
198
- syncDetail = `origin=${remote} (unreachable)`;
199
- }
200
- }
201
- else if (syncIntent === "sync") {
202
- syncStatus = "broken";
203
- syncDetail = "sync was configured but no remote found";
204
- }
205
- }
206
- catch {
207
- if (syncIntent === "sync") {
208
- syncStatus = "broken";
209
- syncDetail = "sync was configured but no remote found";
210
- }
211
- }
212
- const lines = [
213
- `Phren v${version}`,
214
- `Profile: ${activeProfile || "(default)"}`,
215
- machineName ? `Machine: ${machineName}` : null,
216
- `Projects: ${projectCount}`,
217
- `FTS index: ${indexStatus.exists ? `ok (${Math.round((indexStatus.sizeBytes ?? 0) / 1024)} KB)` : "missing"}`,
218
- `MCP: ${mcpEnabled ? "enabled" : "disabled"}`,
219
- `Hooks: ${hooksEnabled ? "enabled" : "disabled"}`,
220
- `Proactivity: ${proactivity}`,
221
- `Task mode: ${taskMode}`,
222
- `Sync: ${syncStatus}${syncStatus !== "synced" ? ` (${syncDetail})` : ""}`,
223
- `Path: ${phrenPath}`,
224
- ].filter(Boolean);
225
- return mcpResponse({
226
- ok: true,
227
- message: lines.join("\n"),
228
- data: {
229
- version,
230
- profile: activeProfile || "(default)",
231
- machine: machineName ?? null,
232
- projectCount,
233
- index: indexStatus,
234
- mcpEnabled,
235
- hooksEnabled,
236
- proactivity,
237
- taskMode,
238
- syncStatus,
239
- syncDetail,
240
- phrenPath,
241
- },
242
- });
243
- });
244
- // ── doctor_fix ─────────────────────────────────────────────────────────────
245
- server.registerTool("doctor_fix", {
246
- title: "◆ phren · doctor fix",
247
- description: "Run phren doctor with --fix: re-links hooks, symlinks, context, and memory pointers. " +
248
- "Returns the list of checks and repair actions taken.",
249
- inputSchema: z.object({
250
- check_data: z.boolean().optional()
251
- .describe("Also validate data files (tasks, findings, governance). Default false."),
252
- }),
253
- }, async ({ check_data }) => {
254
- const { runDoctor } = await import("./link-doctor.js");
255
- const result = await runDoctor(phrenPath, true, check_data ?? false);
256
- const lines = result.checks.map((c) => `${c.ok ? "ok" : "FAIL"} ${c.name}: ${c.detail}`);
257
- const failCount = result.checks.filter((c) => !c.ok).length;
258
- return mcpResponse({
259
- ok: result.ok,
260
- ...(result.ok ? {} : { error: `${failCount} check(s) could not be auto-fixed: ${lines.filter((l) => l.startsWith("FAIL")).join("; ")}` }),
261
- message: result.ok
262
- ? `Doctor fix complete: all ${result.checks.length} checks passed`
263
- : `Doctor fix complete: ${failCount} issue(s) remain`,
264
- data: {
265
- machine: result.machine,
266
- profile: result.profile,
267
- checks: result.checks,
268
- summary: lines.join("\n"),
269
- },
270
- });
271
- });
272
- // ── list_hook_errors ───────────────────────────────────────────────────────
273
- server.registerTool("list_hook_errors", {
274
- title: "◆ phren · hook errors",
275
- description: "List recent error entries from phren hook-errors.log and debug.log. " +
276
- "Useful for diagnosing hook or index failures.",
277
- inputSchema: z.object({
278
- limit: z.number().int().min(1).max(200).optional()
279
- .describe("Max error entries to return (default 20)."),
280
- }),
281
- }, async ({ limit }) => {
282
- const maxEntries = limit ?? 20;
283
- const ERROR_PATTERNS = [
284
- /\berror\b/i,
285
- /\bfail(ed|ure|s)?\b/i,
286
- /\bcrash(ed)?\b/i,
287
- /\btimeout\b/i,
288
- /\bEXCEPTION\b/i,
289
- /\bEACCES\b/,
290
- /\bENOENT\b/,
291
- /\bEPERM\b/,
292
- /\bENOSPC\b/,
293
- ];
294
- function readErrorLines(filePath, filterPatterns) {
295
- try {
296
- if (!fs.existsSync(filePath))
297
- return [];
298
- const content = fs.readFileSync(filePath, "utf8");
299
- const lines = content.split("\n").filter(l => l.trim());
300
- if (!filterPatterns)
301
- return lines; // hook-errors.log: every line is an error
302
- return lines.filter(line => ERROR_PATTERNS.some(p => p.test(line)));
303
- }
304
- catch (err) {
305
- if ((process.env.PHREN_DEBUG))
306
- process.stderr.write(`[phren] readErrorLines: ${errorMessage(err)}\n`);
307
- return [];
308
- }
309
- }
310
- // hook-errors.log contains only hook failure lines (no filtering needed)
311
- const hookErrors = readErrorLines(runtimeFile(phrenPath, "hook-errors.log"), false);
312
- // debug.log may contain non-error lines, so filter
313
- const debugErrors = readErrorLines(runtimeFile(phrenPath, "debug.log"), true);
314
- const allErrors = [...hookErrors, ...debugErrors];
315
- if (allErrors.length === 0) {
316
- return mcpResponse({
317
- ok: true,
318
- message: "No error entries found. Hook errors go to hook-errors.log; general errors require PHREN_DEBUG=1.",
319
- data: { errors: [], total: 0 },
320
- });
321
- }
322
- const recent = allErrors.slice(-maxEntries);
323
- return mcpResponse({
324
- ok: true,
325
- message: `Found ${allErrors.length} error(s), showing last ${recent.length}:\n\n${recent.join("\n")}`,
326
- data: { errors: recent, total: allErrors.length, sources: { hookErrors: hookErrors.length, debugErrors: debugErrors.length } },
327
- });
328
- });
329
- // ── get_review_queue ─────────────────────────────────────────────────────
330
- server.registerTool("get_review_queue", {
331
- title: "◆ phren · get review queue",
332
- description: "List all items in a project's review queue (review.md), or across all projects when omitted. " +
333
- "Returns items with their id, section (Review/Stale/Conflicts), date, text, confidence, and risky flag.",
334
- inputSchema: z.object({
335
- project: z.string().optional().describe("Project name. Omit to read the review queue across all projects in the active profile."),
336
- }),
337
- }, async ({ project }) => {
338
- if (project && !isValidProjectName(project)) {
339
- return mcpResponse({ ok: false, error: `Invalid project name: "${project}".` });
340
- }
341
- if (project) {
342
- const result = readReviewQueue(phrenPath, project);
343
- if (!result.ok) {
344
- return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
345
- }
346
- const items = result.data.map((item) => ({ ...item, project }));
347
- return mcpResponse({
348
- ok: true,
349
- message: `${items.length} queue item(s) for "${project}".`,
350
- data: { items },
351
- });
352
- }
353
- const result = readReviewQueueAcrossProjects(phrenPath, profile);
354
- if (!result.ok) {
355
- return mcpResponse({ ok: false, error: result.error, errorCode: result.code });
356
- }
357
- return mcpResponse({
358
- ok: true,
359
- message: `${result.data.length} queue item(s) across all projects.`,
360
- data: { items: result.data },
361
- });
362
- });
363
- }