@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
@@ -4,6 +4,7 @@ import { execFileSync } from "child_process";
4
4
  import { fileURLToPath } from "url";
5
5
  import { errorMessage } from "./utils.js";
6
6
  import { PACKAGE_NAME, PACKAGE_SPEC } from "./package-metadata.js";
7
+ import { logger } from "./logger.js";
7
8
  function shellCommand(bin) {
8
9
  return process.platform === "win32" ? `${bin}.cmd` : bin;
9
10
  }
@@ -63,8 +64,7 @@ export async function runPhrenUpdate(opts = {}) {
63
64
  }
64
65
  }
65
66
  catch (err) {
66
- if ((process.env.PHREN_DEBUG))
67
- process.stderr.write(`[phren] runPhrenUpdate gitStatus: ${errorMessage(err)}\n`);
67
+ logger.debug("runPhrenUpdate gitStatus", errorMessage(err));
68
68
  }
69
69
  const pull = run("git", ["pull", "--rebase", "--autostash"], root);
70
70
  run(shellCommand("npm"), ["install"], root);
package/mcp/dist/utils.js CHANGED
@@ -5,6 +5,28 @@ import * as yaml from "js-yaml";
5
5
  import { fileURLToPath } from "url";
6
6
  import { findPhrenPath } from "./phren-paths.js";
7
7
  import { bootstrapPhrenDotEnv } from "./phren-dotenv.js";
8
+ // Lazy import of logDebug to break circular dependency:
9
+ // utils.ts -> phren-paths.ts -> logger.ts -> phren-paths.ts -> utils.ts
10
+ let _logDebug;
11
+ async function ensureLogDebug() {
12
+ if (!_logDebug) {
13
+ try {
14
+ const mod = await import("./logger.js");
15
+ _logDebug = mod.logger.debug;
16
+ }
17
+ catch {
18
+ _logDebug = () => { };
19
+ }
20
+ }
21
+ }
22
+ function getLogDebug() {
23
+ if (!_logDebug) {
24
+ // Kick off the async import for future calls; fall back to no-op for this call
25
+ void ensureLogDebug();
26
+ return () => { };
27
+ }
28
+ return _logDebug;
29
+ }
8
30
  const _moduleDir = path.dirname(fileURLToPath(import.meta.url));
9
31
  function loadSynonymsJson(fileName) {
10
32
  const filePath = path.join(_moduleDir, fileName);
@@ -12,8 +34,7 @@ function loadSynonymsJson(fileName) {
12
34
  return JSON.parse(fs.readFileSync(filePath, "utf8"));
13
35
  }
14
36
  catch (err) {
15
- if ((process.env.PHREN_DEBUG))
16
- process.stderr.write(`[phren] ${fileName} load failed: ${err instanceof Error ? err.message : String(err)}\n`);
37
+ getLogDebug()("loadSynonymsJson", `${fileName} load failed: ${err instanceof Error ? err.message : String(err)}`);
17
38
  return {};
18
39
  }
19
40
  }
@@ -148,7 +169,7 @@ export const STOP_WORDS = new Set([
148
169
  export function extractKeywordEntries(text) {
149
170
  const words = text
150
171
  .toLowerCase()
151
- .replace(/[^\w\s-]/g, " ")
172
+ .replace(/[^\p{L}\p{N}\s_-]/gu, " ")
152
173
  .split(/\s+/)
153
174
  .filter(w => w.length > 1 && !STOP_WORDS.has(w));
154
175
  // Build bigrams from adjacent non-stop-words
@@ -233,26 +254,27 @@ export function queueFilePath(phrenPath, project) {
233
254
  }
234
255
  return result;
235
256
  }
257
+ const MAX_FTS_QUERY_LENGTH = 500;
236
258
  // Sanitize user input before passing it to an FTS5 MATCH expression.
237
259
  // Strips FTS5-specific syntax that could cause injection or parse errors.
238
260
  export function sanitizeFts5Query(raw) {
239
261
  if (!raw)
240
262
  return "";
241
- if (raw.length > 500)
242
- raw = raw.slice(0, 500);
243
- // Whitelist approach: only allow alphanumeric, spaces, hyphens, apostrophes, double quotes, asterisks
244
- let q = raw.replace(/[^a-zA-Z0-9 \-"*]/g, " ");
245
- q = q.replace(/\s+/g, " ");
246
- q = q.trim();
247
- // Q83: FTS5 only accepts * as a prefix operator directly attached to a token
248
- // (e.g. "foo*"). A bare trailing asterisk (or lone "*") produces invalid
249
- // FTS5 syntax. Strip any asterisk that is not immediately preceded by a
250
- // word character so the query remains valid.
263
+ if (raw.length > MAX_FTS_QUERY_LENGTH)
264
+ raw = raw.slice(0, MAX_FTS_QUERY_LENGTH);
265
+ // Whitelist approach: only allow alphanumeric, spaces, hyphens, apostrophes, asterisks
266
+ let q = raw.replace(/[^\p{L}\p{N} \-"*]/gu, " ");
267
+ // Strip all double quotes — buildFtsClauses wraps terms in quotes itself,
268
+ // so user-supplied quotes only risk producing unbalanced FTS5 syntax.
269
+ q = q.replace(/"/g, "");
270
+ // Q83: see docs/decisions/Q83-fts5-asterisk-validation.md
251
271
  q = q.replace(/(?<!\w)\*/g, "");
252
272
  // Also strip a trailing asterisk that is preceded only by whitespace at word
253
273
  // end of the whole query (handles "foo *" → "foo").
254
274
  q = q.replace(/\s+\*$/g, "");
255
- return q.trim();
275
+ // Normalize spaces after all stripping to avoid double spaces from removed characters
276
+ q = q.replace(/\s+/g, " ").trim();
277
+ return q;
256
278
  }
257
279
  function parseSynonymsYaml(filePath) {
258
280
  if (!fs.existsSync(filePath))
@@ -276,8 +298,7 @@ function parseSynonymsYaml(filePath) {
276
298
  return loaded;
277
299
  }
278
300
  catch (err) {
279
- if ((process.env.PHREN_DEBUG))
280
- process.stderr.write(`[phren] synonyms.yaml parse failed (${filePath}): ${err instanceof Error ? err.message : String(err)}\n`);
301
+ getLogDebug()("parseSynonymsYaml", `synonyms.yaml parse failed (${filePath}): ${err instanceof Error ? err.message : String(err)}`);
281
302
  return {};
282
303
  }
283
304
  }
@@ -315,8 +336,7 @@ function parseLearnedSynonymsJson(filePath) {
315
336
  return loaded;
316
337
  }
317
338
  catch (err) {
318
- if ((process.env.PHREN_DEBUG))
319
- process.stderr.write(`[phren] learned-synonyms parse failed (${filePath}): ${err instanceof Error ? err.message : String(err)}\n`);
339
+ getLogDebug()("parseLearnedSynonymsJson", `learned-synonyms parse failed (${filePath}): ${err instanceof Error ? err.message : String(err)}`);
320
340
  return {};
321
341
  }
322
342
  }
@@ -512,8 +532,21 @@ export function buildRobustFtsQuery(raw, project, phrenPath) {
512
532
  // nothing; it trades precision for recall while staying in the FTS index.
513
533
  export function buildRelaxedFtsQuery(raw, project, phrenPath) {
514
534
  const clauses = buildFtsClauses(raw, project, phrenPath);
515
- if (clauses.length < 3)
535
+ if (clauses.length === 0)
516
536
  return "";
537
+ // Short queries (1-2 terms): OR the clauses together with prefix expansion
538
+ if (clauses.length === 1) {
539
+ const term = clauses[0];
540
+ // Add prefix wildcard for unquoted-style terms to broaden recall
541
+ const inner = term.replace(/^"(.*)"$/, "$1");
542
+ if (inner.length >= 3) {
543
+ return `(${term} OR "${inner}"*)`;
544
+ }
545
+ return term;
546
+ }
547
+ if (clauses.length === 2) {
548
+ return `(${clauses[0]} OR ${clauses[1]})`;
549
+ }
517
550
  const salientClauses = clauses
518
551
  .map((clause, index) => ({ clause, index, score: clauseSignalScore(clause) }))
519
552
  .sort((a, b) => {
@@ -539,5 +572,16 @@ export function buildFtsQueryVariants(raw, project, phrenPath) {
539
572
  buildRobustFtsQuery(raw, project, phrenPath),
540
573
  buildRelaxedFtsQuery(raw, project, phrenPath),
541
574
  ].filter(Boolean);
575
+ // For short queries, add a prefix-expanded variant to catch partial matches
576
+ const clauses = buildFtsClauses(raw, project, phrenPath);
577
+ if (clauses.length <= 2) {
578
+ const prefixParts = clauses
579
+ .map(c => c.replace(/^"(.*)"$/, "$1"))
580
+ .filter(t => t.length >= 3)
581
+ .map(t => `"${t}"*`);
582
+ if (prefixParts.length > 0) {
583
+ variants.push(prefixParts.join(" OR "));
584
+ }
585
+ }
542
586
  return [...new Set(variants)];
543
587
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phren/cli",
3
- "version": "0.0.28",
3
+ "version": "0.0.33",
4
4
  "description": "Knowledge layer for AI agents. Phren learns and recalls.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,6 @@
16
16
  "dependencies": {
17
17
  "@modelcontextprotocol/sdk": "^1.27.1",
18
18
  "chalk": "^5.6.2",
19
- "esbuild": "^0.27.4",
20
19
  "glob": "^13.0.6",
21
20
  "graphology": "^0.26.0",
22
21
  "graphology-layout-forceatlas2": "^0.10.1",
@@ -27,6 +26,7 @@
27
26
  "zod": "^4.3.6"
28
27
  },
29
28
  "devDependencies": {
29
+ "esbuild": "^0.27.4",
30
30
  "@playwright/test": "^1.58.2",
31
31
  "@types/js-yaml": "^4.0.9",
32
32
  "@types/node": "^25.5.0",
@@ -105,4 +105,35 @@ if (fs.existsSync(copilotHooks)) {
105
105
  try { fs.unlinkSync(copilotHooks); } catch { /* best-effort */ }
106
106
  }
107
107
 
108
+ // ── Codex MCP config (TOML + JSON) ──
109
+ const codexToml = homePath(".codex", "config.toml");
110
+ if (fs.existsSync(codexToml)) {
111
+ try {
112
+ const content = fs.readFileSync(codexToml, "utf8");
113
+ // Remove [mcp_servers.phren] section
114
+ const cleaned = content.replace(/\[mcp_servers\.phren\][^\[]*/, "").trim();
115
+ if (cleaned !== content.trim()) fs.writeFileSync(codexToml, cleaned + "\n");
116
+ } catch { /* best-effort */ }
117
+ }
118
+
119
+ // ── Session wrapper scripts ──
120
+ const localBinDir = homePath(".local", "bin");
121
+ for (const tool of ["copilot", "cursor", "codex"]) {
122
+ const wrapperPath = path.join(localBinDir, tool);
123
+ try {
124
+ if (fs.existsSync(wrapperPath)) {
125
+ const content = fs.readFileSync(wrapperPath, "utf8");
126
+ if (content.includes("PHREN_PATH") && content.includes("phren")) {
127
+ fs.unlinkSync(wrapperPath);
128
+ }
129
+ }
130
+ } catch { /* best-effort */ }
131
+ }
132
+
133
+ // ── Machine context file ──
134
+ const contextFile = homePath(".phren-context.md");
135
+ if (fs.existsSync(contextFile)) {
136
+ try { fs.unlinkSync(contextFile); } catch { /* best-effort */ }
137
+ }
138
+
108
139
  console.log("phren: cleaned up hooks and MCP config from agent settings.");
@@ -19,9 +19,10 @@ These skills are available as a full set via phren, or individually from the Cla
19
19
  | Skill | What it does |
20
20
  |-------|-------------|
21
21
  | `/phren-sync` | Pull phren to a new machine or push config changes back to the repo |
22
- | `/phren-init` | Scaffold a new project with summary, CLAUDE.md, task |
22
+ | `/phren-init` | Scaffold a new project with summary, CLAUDE.md, tasks |
23
23
  | `/phren-discover` | Research what's missing in a project and surface gaps and opportunities |
24
24
  | `/phren-consolidate` | Find patterns across all project FINDINGS.md files |
25
+ | `/phren-profiles` | Manages machine-to-profile mapping |
25
26
 
26
27
  ### Your own skills
27
28
 
@@ -48,7 +49,7 @@ In shared mode, skills and project config live in `~/.phren` (or wherever `PHREN
48
49
  If you're using `phren init --mode project-local`, the root is `<repo>/.phren` instead. Project-local mode does not use profiles, machine mappings, or global hooks.
49
50
 
50
51
  - `~/.phren/global/`: skills and config that apply everywhere
51
- - `~/.phren/<project>/`: per-project CLAUDE.md, skills, task, findings
52
+ - `~/.phren/<project>/`: per-project CLAUDE.md, skills, tasks, findings
52
53
  - `~/.phren/profiles/`: YAML files mapping project sets to machine roles
53
54
  - `~/.phren/machines.yaml`: maps machine hostnames to profiles
54
55
 
@@ -1,22 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { debugLog } from "./shared.js";
4
- import { errorMessage } from "./utils.js";
5
- export function recordRetrieval(phrenPath, file, section) {
6
- const dir = path.join(phrenPath, ".runtime");
7
- fs.mkdirSync(dir, { recursive: true });
8
- const logPath = path.join(dir, "retrieval-log.jsonl");
9
- const entry = { file, section, retrievedAt: new Date().toISOString() };
10
- fs.appendFileSync(logPath, JSON.stringify(entry) + "\n");
11
- try {
12
- const stat = fs.statSync(logPath);
13
- if (stat.size > 500_000) {
14
- const content = fs.readFileSync(logPath, "utf8");
15
- const lines = content.split("\n").filter(Boolean);
16
- fs.writeFileSync(logPath, lines.slice(-1000).join("\n") + "\n");
17
- }
18
- }
19
- catch (err) {
20
- debugLog(`recordRetrieval rotation failed: ${errorMessage(err)}`);
21
- }
22
- }