@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.
- package/mcp/dist/capabilities/cli.js +2 -5
- package/mcp/dist/capabilities/mcp.js +5 -8
- package/mcp/dist/capabilities/types.js +2 -5
- package/mcp/dist/capabilities/vscode.js +2 -5
- package/mcp/dist/capabilities/web-ui.js +2 -5
- package/mcp/dist/{cli-actions.js → cli/actions.js} +25 -21
- package/mcp/dist/{cli.js → cli/cli.js} +13 -13
- package/mcp/dist/{cli-config.js → cli/config.js} +12 -12
- package/mcp/dist/{cli-extract.js → cli/extract.js} +8 -8
- package/mcp/dist/{cli-govern.js → cli/govern.js} +28 -17
- package/mcp/dist/{cli-graph.js → cli/graph.js} +10 -9
- package/mcp/dist/{cli-hooks-citations.js → cli/hooks-citations.js} +2 -2
- package/mcp/dist/{cli-hooks-context.js → cli/hooks-context.js} +23 -23
- package/mcp/dist/{cli-hooks-globs.js → cli/hooks-globs.js} +4 -4
- package/mcp/dist/{cli-hooks-output.js → cli/hooks-output.js} +9 -10
- package/mcp/dist/{cli-hooks-session.js → cli/hooks-session.js} +58 -117
- package/mcp/dist/{cli-hooks.js → cli/hooks.js} +27 -26
- package/mcp/dist/{cli-namespaces.js → cli/namespaces.js} +25 -24
- package/mcp/dist/{cli-ops.js → cli/ops.js} +9 -9
- package/mcp/dist/{cli-search.js → cli/search.js} +12 -11
- package/mcp/dist/cli-hooks-git.js +243 -0
- package/mcp/dist/cli-hooks-prompt.js +323 -0
- package/mcp/dist/cli-hooks-session-handlers.js +337 -0
- package/mcp/dist/cli-hooks-stop.js +519 -0
- package/mcp/dist/{content-archive.js → content/archive.js} +16 -29
- package/mcp/dist/{content-citation.js → content/citation.js} +5 -5
- package/mcp/dist/{content-dedup.js → content/dedup.js} +9 -12
- package/mcp/dist/{content-learning.js → content/learning.js} +41 -20
- package/mcp/dist/{content-validate.js → content/validate.js} +5 -5
- package/mcp/dist/{core-finding.js → core/finding.js} +4 -4
- package/mcp/dist/{core-project.js → core/project.js} +4 -4
- package/mcp/dist/{core-search.js → core/search.js} +2 -2
- package/mcp/dist/{data-access.js → data/access.js} +142 -15
- package/mcp/dist/{data-tasks.js → data/tasks.js} +7 -5
- package/mcp/dist/embedding.js +9 -14
- package/mcp/dist/entrypoint.js +11 -11
- package/mcp/dist/{finding-context.js → finding/context.js} +2 -2
- package/mcp/dist/{finding-impact.js → finding/impact.js} +3 -3
- package/mcp/dist/{finding-journal.js → finding/journal.js} +4 -4
- package/mcp/dist/{finding-lifecycle.js → finding/lifecycle.js} +13 -7
- package/mcp/dist/governance/audit.js +30 -0
- package/mcp/dist/{governance-locks.js → governance/locks.js} +14 -9
- package/mcp/dist/{governance-policy.js → governance/policy.js} +23 -12
- package/mcp/dist/{governance-rbac.js → governance/rbac.js} +4 -4
- package/mcp/dist/{governance-scores.js → governance/scores.js} +10 -11
- package/mcp/dist/hooks.js +53 -37
- package/mcp/dist/index-query.js +4 -1
- package/mcp/dist/index.js +54 -30
- package/mcp/dist/{init-config.js → init/config.js} +6 -6
- package/mcp/dist/{init.js → init/init.js} +80 -69
- package/mcp/dist/{init-preferences.js → init/preferences.js} +3 -3
- package/mcp/dist/{init-setup.js → init/setup.js} +17 -19
- package/mcp/dist/{init-shared.js → init/shared.js} +4 -4
- package/mcp/dist/init-bootstrap.js +21 -0
- package/mcp/dist/init-detect.js +38 -0
- package/mcp/dist/init-env.js +114 -0
- package/mcp/dist/init-fresh.js +234 -0
- package/mcp/dist/init-hooks.js +26 -0
- package/mcp/dist/init-mcp.js +65 -0
- package/mcp/dist/init-modes.js +135 -0
- package/mcp/dist/init-npm.js +37 -0
- package/mcp/dist/init-project-local.js +99 -0
- package/mcp/dist/init-semantic.js +48 -0
- package/mcp/dist/init-types.js +1 -0
- package/mcp/dist/init-uninstall.js +504 -0
- package/mcp/dist/init-update.js +96 -0
- package/mcp/dist/init-walkthrough.js +524 -0
- package/mcp/dist/{link-checksums.js → link/checksums.js} +5 -5
- package/mcp/dist/{link-context.js → link/context.js} +4 -4
- package/mcp/dist/{link-doctor.js → link/doctor.js} +20 -22
- package/mcp/dist/{link.js → link/link.js} +26 -31
- package/mcp/dist/{link-skills.js → link/skills.js} +10 -10
- package/mcp/dist/logger.js +11 -3
- package/mcp/dist/package-metadata.js +1 -1
- package/mcp/dist/phren-art.js +4 -126
- package/mcp/dist/phren-paths.js +30 -12
- package/mcp/dist/proactivity.js +3 -3
- package/mcp/dist/profile-store.js +5 -6
- package/mcp/dist/project-config.js +2 -2
- package/mcp/dist/project-topics.js +17 -47
- package/mcp/dist/provider-adapters.js +1 -1
- package/mcp/dist/query-correlation.js +1 -1
- package/mcp/dist/runtime-profile.js +1 -1
- package/mcp/dist/{session-checkpoints.js → session/checkpoints.js} +3 -3
- package/mcp/dist/{session-utils.js → session/utils.js} +1 -1
- package/mcp/dist/{shared-content.js → shared/content.js} +7 -7
- package/mcp/dist/{shared-data-utils.js → shared/data-utils.js} +28 -3
- package/mcp/dist/{shared-embedding-cache.js → shared/embedding-cache.js} +3 -3
- package/mcp/dist/{shared-fragment-graph.js → shared/fragment-graph.js} +19 -42
- package/mcp/dist/shared/governance.js +4 -0
- package/mcp/dist/{shared-index.js → shared/index.js} +105 -132
- package/mcp/dist/{shared-ollama.js → shared/ollama.js} +25 -7
- package/mcp/dist/shared/process.js +24 -0
- package/mcp/dist/{shared-retrieval.js → shared/retrieval.js} +22 -24
- package/mcp/dist/{shared-search-fallback.js → shared/search-fallback.js} +18 -20
- package/mcp/dist/{shared-sqljs.js → shared/sqljs.js} +3 -3
- package/mcp/dist/{shared-vector-index.js → shared/vector-index.js} +3 -3
- package/mcp/dist/shared.js +6 -60
- package/mcp/dist/{shell-entry.js → shell/entry.js} +6 -6
- package/mcp/dist/{shell-input.js → shell/input.js} +13 -13
- package/mcp/dist/{shell-palette.js → shell/palette.js} +3 -3
- package/mcp/dist/{shell-render.js → shell/render.js} +2 -2
- package/mcp/dist/{shell.js → shell/shell.js} +11 -11
- package/mcp/dist/{shell-state-store.js → shell/state-store.js} +5 -5
- package/mcp/dist/{shell-view-list.js → shell/view-list.js} +1 -1
- package/mcp/dist/{shell-view.js → shell/view.js} +13 -13
- package/mcp/dist/{skill-files.js → skill/files.js} +9 -9
- package/mcp/dist/{skill-registry.js → skill/registry.js} +5 -5
- package/mcp/dist/{skill-state.js → skill/state.js} +1 -4
- package/mcp/dist/startup-embedding.js +2 -2
- package/mcp/dist/status.js +15 -14
- package/mcp/dist/{tasks-github.js → task/github.js} +3 -2
- package/mcp/dist/{task-hygiene.js → task/hygiene.js} +4 -4
- package/mcp/dist/{task-lifecycle.js → task/lifecycle.js} +8 -13
- package/mcp/dist/telemetry.js +3 -4
- package/mcp/dist/tool-registry.js +29 -17
- package/mcp/dist/tools/config.js +530 -0
- package/mcp/dist/{mcp-data.js → tools/data.js} +8 -10
- package/mcp/dist/{mcp-extract-facts.js → tools/extract-facts.js} +6 -6
- package/mcp/dist/{mcp-extract.js → tools/extract.js} +6 -6
- package/mcp/dist/tools/finding.js +584 -0
- package/mcp/dist/{mcp-graph.js → tools/graph.js} +11 -14
- package/mcp/dist/{mcp-hooks.js → tools/hooks.js} +6 -6
- package/mcp/dist/{mcp-memory.js → tools/memory.js} +5 -5
- package/mcp/dist/tools/ops.js +468 -0
- package/mcp/dist/tools/search.js +672 -0
- package/mcp/dist/{mcp-session.js → tools/session.js} +51 -25
- package/mcp/dist/{mcp-skills.js → tools/skills.js} +42 -35
- package/mcp/dist/{mcp-tasks.js → tools/tasks.js} +155 -282
- package/mcp/dist/{memory-ui-data.js → ui/data.js} +31 -17
- package/mcp/dist/{memory-ui.js → ui/memory-ui.js} +3 -3
- package/mcp/dist/{memory-ui-page.js → ui/page.js} +5 -7
- package/mcp/dist/ui/server.js +1024 -0
- package/mcp/dist/update.js +2 -2
- package/mcp/dist/utils.js +63 -19
- package/package.json +2 -2
- package/scripts/preuninstall.mjs +31 -0
- package/starter/global/CLAUDE.md +3 -2
- package/mcp/dist/governance-audit.js +0 -22
- package/mcp/dist/mcp-config.js +0 -551
- package/mcp/dist/mcp-finding.js +0 -594
- package/mcp/dist/mcp-ops.js +0 -363
- package/mcp/dist/mcp-search.js +0 -668
- package/mcp/dist/memory-ui-server.js +0 -1411
- package/mcp/dist/shared-governance.js +0 -4
- /package/mcp/dist/{content-metadata.js → content/metadata.js} +0 -0
- /package/mcp/dist/{shared-stemmer.js → shared/stemmer.js} +0 -0
- /package/mcp/dist/{shell-types.js → shell/types.js} +0 -0
- /package/mcp/dist/{mcp-types.js → tools/types.js} +0 -0
- /package/mcp/dist/{memory-ui-assets.js → ui/assets.js} +0 -0
- /package/mcp/dist/{memory-ui-graph.js → ui/graph.js} +0 -0
- /package/mcp/dist/{memory-ui-scripts.js → ui/scripts.js} +0 -0
- /package/mcp/dist/{memory-ui-styles.js → ui/styles.js} +0 -0
package/mcp/dist/proactivity.js
CHANGED
|
@@ -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
|
|
4
|
+
import { governanceInstallPreferencesFile, readInstallPreferences } from "./init/preferences.js";
|
|
5
5
|
import { errorMessage } from "./utils.js";
|
|
6
|
-
import { getWorkflowPolicy } from "./governance
|
|
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
|
-
|
|
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
|
|
9
|
-
import { withSafeLock } from "./shared
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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
|
|
692
|
-
|
|
693
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,8 +1,8 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import { errorMessage } from "
|
|
4
|
-
import { debugLog, sessionMarker } from "
|
|
5
|
-
import { atomicWriteJson } from "./
|
|
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 "
|
|
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 "
|
|
3
|
-
export { filterTrustedFindings, filterTrustedFindingsDetailed, } from "
|
|
4
|
-
export { scanForSecrets, resolveCoref, isDuplicateFinding, detectConflicts, extractDynamicEntities, checkSemanticDedup, checkSemanticConflicts, } from "
|
|
5
|
-
export { countActiveFindings, autoArchiveToReference, } from "
|
|
6
|
-
export { upsertCanonical, addFindingToFile, addFindingsToFile, autoDetectFindingType, } from "
|
|
7
|
-
export { FINDING_LIFECYCLE_STATUSES, FINDING_TYPE_DECAY, extractFindingType, parseFindingLifecycle, buildLifecycleComments, isInactiveFindingLine, } from "
|
|
8
|
-
export { METADATA_REGEX, parseStatus, parseStatusField, parseSupersession, parseSupersedesRef, parseContradiction, parseAllContradictions, parseFindingId, parseCreatedDate, isCitationLine, isArchiveStart, isArchiveEnd, stripLifecycleMetadata, stripRelationMetadata, stripAllMetadata, stripComments, addMetadata, } from "
|
|
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 "
|
|
4
|
-
import { withFileLock as withFileLockRaw } from "./
|
|
5
|
-
import { isValidProjectName, safeProjectPath, errorMessage } from "
|
|
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 "
|
|
4
|
-
import { withFileLock } from "./
|
|
5
|
-
import { errorMessage } from "
|
|
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 "./
|
|
1
|
+
import { decodeStringRow } from "./index.js";
|
|
2
2
|
import * as fs from "fs";
|
|
3
|
-
import { runtimeFile } from "
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|