@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,9 +1,9 @@
1
1
  import * as fs from "fs";
2
2
  import { bootstrapPhrenDotEnv } from "./phren-dotenv.js";
3
3
  import { debugLog, findPhrenPath } from "./phren-paths.js";
4
- import { governanceInstallPreferencesFile, readInstallPreferences } from "./init-preferences.js";
4
+ import { governanceInstallPreferencesFile, readInstallPreferences } from "./init/preferences.js";
5
5
  import { errorMessage } from "./utils.js";
6
- import { getWorkflowPolicy } from "./governance-policy.js";
6
+ import { getWorkflowPolicy } from "./governance/policy.js";
7
7
  export const PROACTIVITY_LEVELS = ["high", "medium", "low"];
8
8
  const DEFAULT_PROACTIVITY_LEVEL = "high";
9
9
  const EXPLICIT_FINDING_SIGNAL_PATTERN = /\b(add finding|worth remembering)\b/i;
@@ -21,7 +21,7 @@ function resolveProactivityPhrenPath(explicitPhrenPath) {
21
21
  return explicitPhrenPath ?? findPhrenPath();
22
22
  }
23
23
  /** Read per-user preferences from ~/.phren/.users/<actor>/preferences.json. Actor from PHREN_ACTOR env var. */
24
- export function readUserPreferences(explicitPhrenPath) {
24
+ function readUserPreferences(explicitPhrenPath) {
25
25
  const phrenPath = resolveProactivityPhrenPath(explicitPhrenPath);
26
26
  if (!phrenPath)
27
27
  return {};
@@ -5,8 +5,9 @@ import * as yaml from "js-yaml";
5
5
  import { phrenErr, PhrenError, phrenOk, forwardErr, getProjectDirs, readRootManifest, } from "./shared.js";
6
6
  import { defaultMachineName, getMachineName } from "./machine-identity.js";
7
7
  import { errorMessage, isValidProjectName } from "./utils.js";
8
- import { TASK_FILE_ALIASES } from "./data-tasks.js";
9
- import { withSafeLock } from "./shared-data-utils.js";
8
+ import { TASK_FILE_ALIASES } from "./data/tasks.js";
9
+ import { withSafeLock } from "./shared/data-utils.js";
10
+ import { logger } from "./logger.js";
10
11
  export function resolveActiveProfile(phrenPath, requestedProfile) {
11
12
  const manifest = readRootManifest(phrenPath);
12
13
  if (manifest?.installMode === "project-local") {
@@ -61,8 +62,7 @@ export function listMachines(phrenPath) {
61
62
  return phrenOk(cleaned);
62
63
  }
63
64
  catch (err) {
64
- if ((process.env.PHREN_DEBUG))
65
- process.stderr.write(`[phren] listMachines yaml parse: ${errorMessage(err)}\n`);
65
+ logger.debug("profile-store", `listMachines yaml parse: ${errorMessage(err)}`);
66
66
  return phrenErr(`Could not parse machines.yaml. Check the file for syntax errors or run 'phren doctor --fix'.`, PhrenError.MALFORMED_YAML);
67
67
  }
68
68
  }
@@ -226,8 +226,7 @@ export function listProfiles(phrenPath) {
226
226
  profiles.push({ name, file: full, projects, ...(defaults ? { defaults } : {}) });
227
227
  }
228
228
  catch (err) {
229
- if ((process.env.PHREN_DEBUG))
230
- process.stderr.write(`[phren] listProfiles yamlParse: ${errorMessage(err)}\n`);
229
+ logger.debug("profile-store", `listProfiles yamlParse: ${errorMessage(err)}`);
231
230
  return phrenErr(`profiles/${file}`, PhrenError.MALFORMED_YAML);
232
231
  }
233
232
  }
@@ -2,10 +2,10 @@ import * as crypto from "crypto";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
4
  import * as yaml from "js-yaml";
5
- import { readInstallPreferences } from "./init-preferences.js";
5
+ import { readInstallPreferences } from "./init/preferences.js";
6
6
  import { debugLog } from "./shared.js";
7
7
  import { errorMessage, safeProjectPath } from "./utils.js";
8
- import { withFileLock } from "./shared-governance.js";
8
+ import { withFileLock } from "./shared/governance.js";
9
9
  export const PROJECT_OWNERSHIP_MODES = ["phren-managed", "detached", "repo-managed"];
10
10
  export const PROJECT_HOOK_EVENTS = ["UserPromptSubmit", "Stop", "SessionStart", "PostToolUse"];
11
11
  export function parseProjectOwnershipMode(raw) {
@@ -2,8 +2,9 @@ import * as crypto from "crypto";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
4
  import { debugLog } from "./shared.js";
5
- import { withFileLock } from "./shared-governance.js";
5
+ import { withFileLock } from "./shared/governance.js";
6
6
  import { STOP_WORDS, errorMessage, extractKeywords, isValidProjectName, safeProjectPath } from "./utils.js";
7
+ import { walkDirectory } from "./shared/data-utils.js";
7
8
  const TOPIC_CONFIG_FILENAME = "topic-config.json";
8
9
  const AUTO_TOPIC_MARKER_RE = /^<!--\s*phren:auto-topic(?:\s+slug=([a-z0-9_-]+))?\s*-->$/;
9
10
  const ARCHIVED_SECTION_RE = /^## Archived (\d{4}-\d{2}-\d{2})$/;
@@ -303,7 +304,7 @@ function normalizeKeyword(raw) {
303
304
  .replace(/\s+/g, " ")
304
305
  .trim();
305
306
  }
306
- export function normalizeTopicSlug(raw) {
307
+ function normalizeTopicSlug(raw) {
307
308
  return raw
308
309
  .trim()
309
310
  .toLowerCase()
@@ -384,10 +385,7 @@ function topicConfigPath(phrenPath, project) {
384
385
  function projectDirPath(phrenPath, project) {
385
386
  return safeProjectPath(phrenPath, project);
386
387
  }
387
- export function topicReferenceDir(phrenPath, project) {
388
- return safeProjectPath(phrenPath, project, "reference", "topics");
389
- }
390
- export function topicReferenceRelativePath(slug) {
388
+ function topicReferenceRelativePath(slug) {
391
389
  return path.posix.join("reference", "topics", `${slug}.md`);
392
390
  }
393
391
  export function topicReferencePath(phrenPath, project, slug) {
@@ -575,7 +573,7 @@ export function readProjectTopics(phrenPath, project) {
575
573
  }
576
574
  return { source: "custom", topics: normalized, domain: typeof parsed.domain === "string" ? parsed.domain : undefined };
577
575
  }
578
- export function readPinnedTopics(phrenPath, project) {
576
+ function readPinnedTopics(phrenPath, project) {
579
577
  const configPath = topicConfigPath(phrenPath, project);
580
578
  if (!configPath || !fs.existsSync(configPath))
581
579
  return [];
@@ -675,27 +673,14 @@ function normalizeBullet(line) {
675
673
  }
676
674
  function collectArchivedBulletsRecursively(dirPath) {
677
675
  const bullets = new Set();
678
- if (!fs.existsSync(dirPath))
679
- return bullets;
680
- const stack = [dirPath];
681
- while (stack.length > 0) {
682
- const current = stack.pop();
683
- for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
684
- const fullPath = path.join(current, entry.name);
685
- if (entry.isDirectory()) {
686
- stack.push(fullPath);
687
- continue;
688
- }
689
- if (!entry.isFile() || !entry.name.endsWith(".md"))
676
+ for (const filePath of walkDirectory(dirPath)) {
677
+ const content = fs.readFileSync(filePath, "utf8");
678
+ for (const line of content.split("\n")) {
679
+ if (!line.startsWith("- "))
690
680
  continue;
691
- const content = fs.readFileSync(fullPath, "utf8");
692
- for (const line of content.split("\n")) {
693
- if (!line.startsWith("- "))
694
- continue;
695
- const normalized = normalizeBullet(line);
696
- if (normalized)
697
- bullets.add(normalized);
698
- }
681
+ const normalized = normalizeBullet(line);
682
+ if (normalized)
683
+ bullets.add(normalized);
699
684
  }
700
685
  }
701
686
  return bullets;
@@ -797,23 +782,7 @@ function parseLegacyTopicEntries(content, project) {
797
782
  return { slug: fallbackSlug, entries };
798
783
  }
799
784
  function readReferenceMarkdownFiles(referenceDir) {
800
- if (!fs.existsSync(referenceDir))
801
- return [];
802
- const files = [];
803
- const stack = [referenceDir];
804
- while (stack.length > 0) {
805
- const current = stack.pop();
806
- for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
807
- const fullPath = path.join(current, entry.name);
808
- if (entry.isDirectory()) {
809
- stack.push(fullPath);
810
- continue;
811
- }
812
- if (entry.isFile() && entry.name.endsWith(".md"))
813
- files.push(fullPath);
814
- }
815
- }
816
- return files.sort();
785
+ return walkDirectory(referenceDir).sort();
817
786
  }
818
787
  function relativeToProject(projectDir, filePath) {
819
788
  return path.relative(projectDir, filePath).replace(/\\/g, "/");
@@ -835,7 +804,7 @@ function safeStatIso(filePath) {
835
804
  return "";
836
805
  }
837
806
  }
838
- export function listProjectTopicDocs(phrenPath, project, topics) {
807
+ function listProjectTopicDocs(phrenPath, project, topics) {
839
808
  const projectDir = projectDirPath(phrenPath, project);
840
809
  if (!projectDir)
841
810
  return [];
@@ -885,7 +854,7 @@ export function listProjectReferenceDocs(phrenPath, project, topics) {
885
854
  }
886
855
  return { topicDocs, otherDocs };
887
856
  }
888
- export function listLegacyTopicDocs(phrenPath, project) {
857
+ function listLegacyTopicDocs(phrenPath, project) {
889
858
  const projectDir = projectDirPath(phrenPath, project);
890
859
  const referenceDir = safeProjectPath(phrenPath, project, "reference");
891
860
  if (!projectDir || !referenceDir || !fs.existsSync(referenceDir))
@@ -1042,6 +1011,7 @@ export function suggestTopics(phrenPath, project, topics) {
1042
1011
  }
1043
1012
  return deduped;
1044
1013
  }
1014
+ /** @internal Exported for tests. */
1045
1015
  export const suggestProjectTopics = suggestTopics;
1046
1016
  export function getProjectTopicsResponse(phrenPath, project) {
1047
1017
  const { source, topics } = readProjectTopics(phrenPath, project);
@@ -1054,7 +1024,7 @@ export function getProjectTopicsResponse(phrenPath, project) {
1054
1024
  topicDocs: listProjectTopicDocs(phrenPath, project, topics),
1055
1025
  };
1056
1026
  }
1057
- export function resolveReferenceContentPath(phrenPath, project, file) {
1027
+ function resolveReferenceContentPath(phrenPath, project, file) {
1058
1028
  if (!isValidProjectName(project) || !file || file.includes("\0"))
1059
1029
  return null;
1060
1030
  if (!file.endsWith(".md"))
@@ -35,7 +35,7 @@ function normalizeWindowsPathToWsl(input) {
35
35
  function uniqStrings(values) {
36
36
  return Array.from(new Set(values.filter((value) => Boolean(value && value.trim()))));
37
37
  }
38
- export function pickExistingFile(candidates) {
38
+ function pickExistingFile(candidates) {
39
39
  for (const candidate of candidates) {
40
40
  if (fs.existsSync(candidate))
41
41
  return candidate;
@@ -7,7 +7,7 @@
7
7
  import * as fs from "fs";
8
8
  import { runtimeFile, debugLog } from "./shared.js";
9
9
  import { isFeatureEnabled, errorMessage } from "./utils.js";
10
- import { withFileLock } from "./shared-governance.js";
10
+ import { withFileLock } from "./shared/governance.js";
11
11
  const CORRELATION_FILENAME = "query-correlations.jsonl";
12
12
  const RECENT_WINDOW = 500;
13
13
  const MIN_TOKEN_OVERLAP = 2;
@@ -1,5 +1,5 @@
1
1
  import { resolveActiveProfile } from "./profile-store.js";
2
- export function requestedProfileFromEnv() {
2
+ function requestedProfileFromEnv() {
3
3
  const profile = (process.env.PHREN_PROFILE)?.trim();
4
4
  return profile ? profile : undefined;
5
5
  }
@@ -1,8 +1,8 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
- import { errorMessage } from "./utils.js";
4
- import { debugLog, sessionMarker } from "./shared.js";
5
- import { atomicWriteJson } from "./session-utils.js";
3
+ import { errorMessage } from "../utils.js";
4
+ import { debugLog, sessionMarker } from "../shared.js";
5
+ import { atomicWriteJson } from "./utils.js";
6
6
  function sanitizeFileSegment(value) {
7
7
  const trimmed = value.trim();
8
8
  const safe = trimmed.replace(/[^a-zA-Z0-9._-]+/g, "_");
@@ -1,6 +1,6 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
- import { errorMessage } from "./utils.js";
3
+ import { errorMessage } from "../utils.js";
4
4
  /**
5
5
  * Write JSON to a file atomically using temp-file + rename.
6
6
  * Ensures the parent directory exists before writing.
@@ -1,8 +1,8 @@
1
1
  // Barrel re-export. Internal code imports from the specific modules directly.
2
- export { checkConsolidationNeeded, validateFindingsFormat, stripTaskDoneSection, validateTaskFormat, extractConflictVersions, mergeFindings, mergeTask, autoMergeConflicts, } from "./content-validate.js";
3
- export { filterTrustedFindings, filterTrustedFindingsDetailed, } from "./content-citation.js";
4
- export { scanForSecrets, resolveCoref, isDuplicateFinding, detectConflicts, extractDynamicEntities, checkSemanticDedup, checkSemanticConflicts, } from "./content-dedup.js";
5
- export { countActiveFindings, autoArchiveToReference, } from "./content-archive.js";
6
- export { upsertCanonical, addFindingToFile, addFindingsToFile, autoDetectFindingType, } from "./content-learning.js";
7
- export { FINDING_LIFECYCLE_STATUSES, FINDING_TYPE_DECAY, extractFindingType, parseFindingLifecycle, buildLifecycleComments, isInactiveFindingLine, } from "./finding-lifecycle.js";
8
- export { METADATA_REGEX, parseStatus, parseStatusField, parseSupersession, parseSupersedesRef, parseContradiction, parseAllContradictions, parseFindingId, parseCreatedDate, isCitationLine, isArchiveStart, isArchiveEnd, stripLifecycleMetadata, stripRelationMetadata, stripAllMetadata, stripComments, addMetadata, } from "./content-metadata.js";
2
+ export { checkConsolidationNeeded, validateFindingsFormat, stripTaskDoneSection, validateTaskFormat, extractConflictVersions, mergeFindings, mergeTask, autoMergeConflicts, } from "../content/validate.js";
3
+ export { filterTrustedFindings, filterTrustedFindingsDetailed, } from "../content/citation.js";
4
+ export { scanForSecrets, resolveCoref, isDuplicateFinding, detectConflicts, extractDynamicEntities, checkSemanticDedup, checkSemanticConflicts, } from "../content/dedup.js";
5
+ export { countActiveFindings, autoArchiveToReference, } from "../content/archive.js";
6
+ export { upsertCanonical, addFindingToFile, addFindingsToFile, autoDetectFindingType, } from "../content/learning.js";
7
+ export { FINDING_LIFECYCLE_STATUSES, FINDING_TYPE_DECAY, extractFindingType, parseFindingLifecycle, buildLifecycleComments, isInactiveFindingLine, } from "../finding/lifecycle.js";
8
+ export { METADATA_REGEX, parseStatus, parseStatusField, parseSupersession, parseSupersedesRef, parseContradiction, parseAllContradictions, parseFindingId, parseCreatedDate, isCitationLine, isArchiveStart, isArchiveEnd, stripLifecycleMetadata, stripRelationMetadata, stripAllMetadata, stripComments, addMetadata, } from "../content/metadata.js";
@@ -1,8 +1,8 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
- import { phrenErr, PhrenError, phrenOk, } from "./shared.js";
4
- import { withFileLock as withFileLockRaw } from "./shared-governance.js";
5
- import { isValidProjectName, safeProjectPath, errorMessage } from "./utils.js";
3
+ import { phrenErr, PhrenError, phrenOk, } from "../shared.js";
4
+ import { withFileLock as withFileLockRaw } from "./governance.js";
5
+ import { isValidProjectName, safeProjectPath, errorMessage } from "../utils.js";
6
6
  export function withSafeLock(filePath, fn) {
7
7
  try {
8
8
  return withFileLockRaw(filePath, fn);
@@ -15,6 +15,31 @@ export function withSafeLock(filePath, fn) {
15
15
  throw err;
16
16
  }
17
17
  }
18
+ /**
19
+ * Recursively walk a directory and return paths of files matching an optional filter.
20
+ * Defaults to `.md` files only. Uses an iterative stack to avoid recursion limits.
21
+ */
22
+ export function walkDirectory(root, filter) {
23
+ const accept = filter ?? ((name) => name.endsWith(".md"));
24
+ const results = [];
25
+ if (!fs.existsSync(root))
26
+ return results;
27
+ const stack = [root];
28
+ while (stack.length > 0) {
29
+ const current = stack.pop();
30
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
31
+ const fullPath = path.join(current, entry.name);
32
+ if (entry.isDirectory()) {
33
+ stack.push(fullPath);
34
+ continue;
35
+ }
36
+ if (entry.isFile() && accept(entry.name)) {
37
+ results.push(fullPath);
38
+ }
39
+ }
40
+ }
41
+ return results;
42
+ }
18
43
  export function ensureProject(phrenPath, project) {
19
44
  if (!isValidProjectName(project))
20
45
  return phrenErr(`Project name "${project}" is not valid. Use lowercase letters, numbers, and hyphens (e.g. "my-project").`, PhrenError.INVALID_PROJECT_NAME);
@@ -1,8 +1,8 @@
1
1
  import * as fs from "fs";
2
2
  import * as crypto from "crypto";
3
- import { runtimeFile, debugLog } from "./shared.js";
4
- import { withFileLock } from "./shared-governance.js";
5
- import { errorMessage } from "./utils.js";
3
+ import { runtimeFile, debugLog } from "../shared.js";
4
+ import { withFileLock } from "./governance.js";
5
+ import { errorMessage } from "../utils.js";
6
6
  function isEmbeddingEntry(value) {
7
7
  if (!value || typeof value !== "object" || Array.isArray(value))
8
8
  return false;
@@ -1,10 +1,13 @@
1
- import { decodeStringRow } from "./shared-index.js";
1
+ import { decodeStringRow } from "./index.js";
2
2
  import * as fs from "fs";
3
- import { runtimeFile } from "./shared.js";
4
- import { UNIVERSAL_TECH_TERMS_RE } from "./phren-core.js";
5
- import { errorMessage } from "./utils.js";
3
+ import { runtimeFile } from "../shared.js";
4
+ import { logger } from "../logger.js";
5
+ import { UNIVERSAL_TECH_TERMS_RE } from "../phren-core.js";
6
+ import { errorMessage } from "../utils.js";
7
+ /** @internal Exported for tests. */
6
8
  export function escapeRegex(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
7
- /** Escape SQL LIKE wildcard characters so user input is treated literally. */
9
+ /** Escape SQL LIKE wildcard characters so user input is treated literally.
10
+ * @internal Exported for tests. */
8
11
  export function escapeLike(s) { return s.replace(/[%_\\]/g, '\\$&'); }
9
12
  /**
10
13
  * Log fragment resolution misses to .runtime/fragment-misses.jsonl.
@@ -40,8 +43,6 @@ export function logFragmentMiss(phrenPath, name, context, project) {
40
43
  // Best-effort logging; don't let miss tracking break the caller.
41
44
  }
42
45
  }
43
- /** @deprecated Use logFragmentMiss instead */
44
- export const logEntityMiss = logFragmentMiss;
45
46
  // Use the shared universal starter set. Framework/tool specifics are learned
46
47
  // dynamically per project via extractDynamicFragments() in content-dedup.ts.
47
48
  const PROSE_FRAGMENT_PATTERN = UNIVERSAL_TECH_TERMS_RE;
@@ -109,15 +110,12 @@ export function extractFragmentNames(content) {
109
110
  }
110
111
  return [...found];
111
112
  }
112
- /** @deprecated Use extractFragmentNames instead */
113
- export const extractEntityNames = extractFragmentNames;
114
113
  function getOrCreateFragment(db, name, type) {
115
114
  try {
116
115
  db.run("INSERT OR IGNORE INTO entities (name, type, first_seen_at) VALUES (?, ?, ?)", [name, type, new Date().toISOString().slice(0, 10)]);
117
116
  }
118
117
  catch (err) {
119
- if (process.env.PHREN_DEBUG)
120
- process.stderr.write(`[phren] fragmentInsert: ${errorMessage(err)}\n`);
118
+ logger.debug("fragmentInsert", errorMessage(err));
121
119
  }
122
120
  const result = db.exec("SELECT id FROM entities WHERE name = ? AND type = ?", [name, type]);
123
121
  if (result?.length && result[0]?.values?.length) {
@@ -139,8 +137,7 @@ export function ensureGlobalEntitiesTable(db) {
139
137
  )`);
140
138
  }
141
139
  catch (err) {
142
- if (process.env.PHREN_DEBUG)
143
- process.stderr.write(`[phren] ensureGlobalEntitiesTable: ${errorMessage(err)}\n`);
140
+ logger.debug("ensureGlobalEntitiesTable", errorMessage(err));
144
141
  }
145
142
  }
146
143
  /**
@@ -186,14 +183,11 @@ export function beginUserFragmentBuildCache(phrenPath, projects) {
186
183
  _buildUserFragmentCache.set(cacheKey, loaded.fragments);
187
184
  }
188
185
  catch (err) {
189
- if (process.env.PHREN_DEBUG)
190
- process.stderr.write(`[phren] beginUserFragmentBuildCache: ${errorMessage(err)}\n`);
186
+ logger.debug("beginUserFragmentBuildCache", errorMessage(err));
191
187
  _buildUserFragmentCache.set(cacheKey, []);
192
188
  }
193
189
  }
194
190
  }
195
- /** @deprecated Use beginUserFragmentBuildCache instead */
196
- export const beginUserEntityBuildCache = beginUserFragmentBuildCache;
197
191
  /** End a build-scoped cache created by beginUserFragmentBuildCache(). */
198
192
  export function endUserFragmentBuildCache(phrenPath) {
199
193
  const prefix = `${phrenPath}/`;
@@ -204,8 +198,6 @@ export function endUserFragmentBuildCache(phrenPath) {
204
198
  if (_activeBuildCacheKeyPrefix === prefix)
205
199
  _activeBuildCacheKeyPrefix = null;
206
200
  }
207
- /** @deprecated Use endUserFragmentBuildCache instead */
208
- export const endUserEntityBuildCache = endUserFragmentBuildCache;
209
201
  function parseUserDefinedFragments(phrenPath, project) {
210
202
  const claudeMdPath = `${phrenPath}/${project}/CLAUDE.md`;
211
203
  const cacheKey = `${phrenPath}/${project}`;
@@ -225,8 +217,7 @@ function parseUserDefinedFragments(phrenPath, project) {
225
217
  }
226
218
  }
227
219
  catch (err) {
228
- if (process.env.PHREN_DEBUG)
229
- process.stderr.write(`[phren] parseUserDefinedFragments statCheck: ${errorMessage(err)}\n`);
220
+ logger.debug("parseUserDefinedFragments statCheck", errorMessage(err));
230
221
  }
231
222
  }
232
223
  const loaded = readUserDefinedFragmentsFromDisk(claudeMdPath);
@@ -236,19 +227,16 @@ function parseUserDefinedFragments(phrenPath, project) {
236
227
  return loaded.fragments;
237
228
  }
238
229
  catch (err) {
239
- if (process.env.PHREN_DEBUG)
240
- process.stderr.write(`[phren] parseUserDefinedFragments: ${errorMessage(err)}\n`);
230
+ logger.debug("parseUserDefinedFragments", errorMessage(err));
241
231
  return [];
242
232
  }
243
233
  }
244
234
  /** Clear the user fragment cache (call between index builds). */
245
- export function clearUserFragmentCache() {
235
+ function clearUserFragmentCache() {
246
236
  _userFragmentCache.clear();
247
237
  _buildUserFragmentCache.clear();
248
238
  _activeBuildCacheKeyPrefix = null;
249
239
  }
250
- /** @deprecated Use clearUserFragmentCache instead */
251
- export const clearUserEntityCache = clearUserFragmentCache;
252
240
  // Words that commonly start sentences or appear in titles — not fragment names
253
241
  const SENTENCE_START_WORDS = new Set([
254
242
  "the", "this", "that", "these", "those", "when", "where", "which", "while",
@@ -353,8 +341,7 @@ export function extractAndLinkFragments(db, content, sourceDoc, phrenPath) {
353
341
  db.run("INSERT OR IGNORE INTO entity_links (source_id, target_id, rel_type, source_doc) VALUES (?, ?, ?, ?)", [docFragmentId, fragmentId, "mentions", sourceDoc]);
354
342
  }
355
343
  catch (err) {
356
- if (process.env.PHREN_DEBUG)
357
- process.stderr.write(`[phren] fragmentLinksInsert: ${errorMessage(err)}\n`);
344
+ logger.debug("fragmentLinksInsert", errorMessage(err));
358
345
  }
359
346
  // Write to global_entities for cross-project queries
360
347
  if (project) {
@@ -362,14 +349,11 @@ export function extractAndLinkFragments(db, content, sourceDoc, phrenPath) {
362
349
  db.run("INSERT OR IGNORE INTO global_entities (entity, project, doc_key) VALUES (?, ?, ?)", [name, project, sourceDoc]);
363
350
  }
364
351
  catch (err) {
365
- if (process.env.PHREN_DEBUG)
366
- process.stderr.write(`[phren] globalFragmentsInsert: ${errorMessage(err)}\n`);
352
+ logger.debug("globalFragmentsInsert", errorMessage(err));
367
353
  }
368
354
  }
369
355
  }
370
356
  }
371
- /** @deprecated Use extractAndLinkFragments instead */
372
- export const extractAndLinkEntities = extractAndLinkFragments;
373
357
  /**
374
358
  * Query related fragments for a given name.
375
359
  */
@@ -391,13 +375,10 @@ export function queryFragmentLinks(db, name) {
391
375
  }
392
376
  }
393
377
  catch (err) {
394
- if (process.env.PHREN_DEBUG)
395
- process.stderr.write(`[phren] queryFragmentLinks: ${errorMessage(err)}\n`);
378
+ logger.debug("queryFragmentLinks", errorMessage(err));
396
379
  }
397
380
  return { related };
398
381
  }
399
- /** @deprecated Use queryFragmentLinks instead */
400
- export const queryEntityLinks = queryFragmentLinks;
401
382
  /**
402
383
  * Query cross-project fragment relationships.
403
384
  * Returns projects and docs that share fragments with the given query.
@@ -427,8 +408,7 @@ export function queryCrossProjectFragments(db, fragmentName, excludeProject) {
427
408
  }
428
409
  }
429
410
  catch (err) {
430
- if (process.env.PHREN_DEBUG)
431
- process.stderr.write(`[phren] queryCrossProjectFragments: ${errorMessage(err)}\n`);
411
+ logger.debug("queryCrossProjectFragments", errorMessage(err));
432
412
  }
433
413
  return results;
434
414
  }
@@ -448,10 +428,7 @@ export function getFragmentBoostDocs(db, query) {
448
428
  return boostDocs;
449
429
  }
450
430
  catch (err) {
451
- if (process.env.PHREN_DEBUG)
452
- process.stderr.write(`[phren] getFragmentBoostDocs: ${errorMessage(err)}\n`);
431
+ logger.debug("getFragmentBoostDocs", errorMessage(err));
453
432
  return new Set();
454
433
  }
455
434
  }
456
- /** @deprecated Use getFragmentBoostDocs instead */
457
- export const getEntityBoostDocs = getFragmentBoostDocs;
@@ -0,0 +1,4 @@
1
+ export * from '../governance/policy.js';
2
+ export * from '../governance/scores.js';
3
+ export * from '../governance/audit.js';
4
+ export { withFileLock, isFiniteNumber, hasValidSchemaVersion } from '../governance/locks.js';