@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
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { execFileSync } from "child_process";
|
|
4
|
-
import { debugLog, EXEC_TIMEOUT_QUICK_MS, getProjectDirs, isRecord, homeDir, homePath, hookConfigPath, runtimeHealthFile, } from "
|
|
5
|
-
import { commandVersion, versionAtLeast, nearestWritableTarget } from "
|
|
6
|
-
import { validateGovernanceJson } from "
|
|
7
|
-
import { errorMessage } from "
|
|
8
|
-
import { buildIndex, queryRows } from "
|
|
9
|
-
import { validateTaskFormat, validateFindingsFormat } from "
|
|
10
|
-
import { detectInstalledTools } from "
|
|
11
|
-
import { validateSkillFrontmatter, validateSkillsDir } from "./
|
|
12
|
-
import { verifyFileChecksums, updateFileChecksums } from "./
|
|
13
|
-
import { buildSkillManifest } from "
|
|
14
|
-
import { inspectTaskHygiene } from "
|
|
15
|
-
import { resolveTaskFilePath, TASK_FILE_ALIASES } from "
|
|
16
|
-
import { repairPreexistingInstall } from "
|
|
4
|
+
import { debugLog, EXEC_TIMEOUT_QUICK_MS, getProjectDirs, isRecord, homeDir, homePath, hookConfigPath, runtimeHealthFile, } from "../shared.js";
|
|
5
|
+
import { commandVersion, versionAtLeast, nearestWritableTarget } from "../init/shared.js";
|
|
6
|
+
import { validateGovernanceJson } from "../shared/governance.js";
|
|
7
|
+
import { errorMessage } from "../utils.js";
|
|
8
|
+
import { buildIndex, queryRows } from "../shared/index.js";
|
|
9
|
+
import { validateTaskFormat, validateFindingsFormat } from "../shared/content.js";
|
|
10
|
+
import { detectInstalledTools } from "../hooks.js";
|
|
11
|
+
import { validateSkillFrontmatter, validateSkillsDir } from "./skills.js";
|
|
12
|
+
import { verifyFileChecksums, updateFileChecksums } from "./checksums.js";
|
|
13
|
+
import { buildSkillManifest } from "../skill/registry.js";
|
|
14
|
+
import { inspectTaskHygiene } from "../task/hygiene.js";
|
|
15
|
+
import { resolveTaskFilePath, TASK_FILE_ALIASES } from "../data/tasks.js";
|
|
16
|
+
import { repairPreexistingInstall } from "../init/setup.js";
|
|
17
17
|
import { getMachineName, lookupProfile, findProfileFile, getProfileProjects, findProjectDir, } from "./link.js";
|
|
18
|
-
import { claudeProjectKey } from "./
|
|
19
|
-
import { getProjectOwnershipMode, readProjectConfig } from "
|
|
20
|
-
import { readInstallPreferences } from "
|
|
18
|
+
import { claudeProjectKey } from "./context.js";
|
|
19
|
+
import { getProjectOwnershipMode, readProjectConfig } from "../project-config.js";
|
|
20
|
+
import { readInstallPreferences } from "../init/preferences.js";
|
|
21
|
+
import { logger } from "../logger.js";
|
|
21
22
|
// ── Doctor ──────────────────────────────────────────────────────────────────
|
|
22
23
|
function isWrapperActive(tool) {
|
|
23
24
|
const wrapperPath = homePath(".local", "bin", tool);
|
|
@@ -176,15 +177,13 @@ export async function runDoctor(phrenPath, fix = false, checkData = false) {
|
|
|
176
177
|
fsMs = Date.now() - t0;
|
|
177
178
|
}
|
|
178
179
|
catch (err) {
|
|
179
|
-
|
|
180
|
-
process.stderr.write(`[phren] doctor fsBenchmark: ${errorMessage(err)}\n`);
|
|
180
|
+
logger.debug("doctor", `doctor fsBenchmark: ${errorMessage(err)}`);
|
|
181
181
|
fsMs = -1;
|
|
182
182
|
try {
|
|
183
183
|
fs.unlinkSync(fsBenchFile);
|
|
184
184
|
}
|
|
185
185
|
catch (e2) {
|
|
186
|
-
|
|
187
|
-
process.stderr.write(`[phren] doctor fsBenchmarkCleanup: ${e2 instanceof Error ? e2.message : String(e2)}\n`);
|
|
186
|
+
logger.debug("doctor", `doctor fsBenchmarkCleanup: ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
188
187
|
}
|
|
189
188
|
}
|
|
190
189
|
const fsSlow = fsMs > 500 || fsMs < 0;
|
|
@@ -306,8 +305,7 @@ export async function runDoctor(phrenPath, fix = false, checkData = false) {
|
|
|
306
305
|
runtime = JSON.parse(fs.readFileSync(runtimeHealthPath, "utf8"));
|
|
307
306
|
}
|
|
308
307
|
catch (err) {
|
|
309
|
-
|
|
310
|
-
process.stderr.write(`[phren] doctor runtimeHealth: ${errorMessage(err)}\n`);
|
|
308
|
+
logger.debug("doctor", `doctor runtimeHealth: ${errorMessage(err)}`);
|
|
311
309
|
runtime = null;
|
|
312
310
|
}
|
|
313
311
|
}
|
|
@@ -3,27 +3,28 @@ import * as path from "path";
|
|
|
3
3
|
import * as readline from "readline";
|
|
4
4
|
import * as yaml from "js-yaml";
|
|
5
5
|
import { execFileSync } from "child_process";
|
|
6
|
-
import { ROOT } from "
|
|
7
|
-
import { configureMcpTargets, ensureGovernanceFiles, getHooksEnabledPreference, getMcpEnabledPreference, isVersionNewer, patchJsonFile, setMcpEnabledPreference, } from "
|
|
8
|
-
import { configureAllHooks, detectInstalledTools } from "
|
|
9
|
-
import { getMachineName, persistMachineName } from "
|
|
10
|
-
import { debugLog, EXEC_TIMEOUT_MS, EXEC_TIMEOUT_QUICK_MS, isRecord, homePath, hookConfigPath, installPreferencesFile, atomicWriteText, } from "
|
|
11
|
-
import { errorMessage } from "
|
|
12
|
-
import { log } from "
|
|
13
|
-
import { listMachines as listMachinesShared, listProfiles as listProfilesShared, setMachineProfile, } from "
|
|
14
|
-
import { writeSkillMd, isManagedSymlink } from "./
|
|
15
|
-
import { syncScopeSkillsToDir } from "
|
|
16
|
-
import { renderSkillInstructionsSection } from "
|
|
17
|
-
import { findProjectDir } from "
|
|
18
|
-
import { getProjectOwnershipMode, readProjectConfig, } from "
|
|
19
|
-
import { writeContextDefault, writeContextDebugging, writeContextPlanning, writeContextClean, readBackNativeMemory, rebuildMemory, } from "./
|
|
6
|
+
import { ROOT } from "../package-metadata.js";
|
|
7
|
+
import { configureMcpTargets, ensureGovernanceFiles, getHooksEnabledPreference, getMcpEnabledPreference, isVersionNewer, patchJsonFile, setMcpEnabledPreference, } from "../init/init.js";
|
|
8
|
+
import { configureAllHooks, detectInstalledTools } from "../hooks.js";
|
|
9
|
+
import { getMachineName, persistMachineName } from "../machine-identity.js";
|
|
10
|
+
import { debugLog, EXEC_TIMEOUT_MS, EXEC_TIMEOUT_QUICK_MS, isRecord, homePath, hookConfigPath, installPreferencesFile, atomicWriteText, } from "../shared.js";
|
|
11
|
+
import { errorMessage } from "../utils.js";
|
|
12
|
+
import { log } from "../init/shared.js";
|
|
13
|
+
import { listMachines as listMachinesShared, listProfiles as listProfilesShared, setMachineProfile, } from "../profile-store.js";
|
|
14
|
+
import { writeSkillMd, isManagedSymlink } from "./skills.js";
|
|
15
|
+
import { syncScopeSkillsToDir } from "../skill/files.js";
|
|
16
|
+
import { renderSkillInstructionsSection } from "../skill/registry.js";
|
|
17
|
+
import { findProjectDir } from "../project-locator.js";
|
|
18
|
+
import { getProjectOwnershipMode, readProjectConfig, } from "../project-config.js";
|
|
19
|
+
import { writeContextDefault, writeContextDebugging, writeContextPlanning, writeContextClean, readBackNativeMemory, rebuildMemory, } from "./context.js";
|
|
20
|
+
import { logger } from "../logger.js";
|
|
20
21
|
// Re-export sub-modules so existing imports from "./link.js" continue to work
|
|
21
|
-
export { runDoctor } from "./
|
|
22
|
-
export { updateFileChecksums, verifyFileChecksums } from "./
|
|
23
|
-
export { findProjectDir } from "
|
|
24
|
-
export { parseSkillFrontmatter, validateSkillFrontmatter, validateSkillsDir, readSkillManifestHooks, } from "./
|
|
22
|
+
export { runDoctor } from "./doctor.js";
|
|
23
|
+
export { updateFileChecksums, verifyFileChecksums } from "./checksums.js";
|
|
24
|
+
export { findProjectDir } from "../project-locator.js";
|
|
25
|
+
export { parseSkillFrontmatter, validateSkillFrontmatter, validateSkillsDir, readSkillManifestHooks, } from "./skills.js";
|
|
25
26
|
// ── Helpers (exported for link-doctor) ──────────────────────────────────────
|
|
26
|
-
export { getMachineName } from "
|
|
27
|
+
export { getMachineName } from "../machine-identity.js";
|
|
27
28
|
export function lookupProfile(phrenPath, machine) {
|
|
28
29
|
const listed = listMachinesShared(phrenPath);
|
|
29
30
|
if (!listed.ok)
|
|
@@ -115,8 +116,7 @@ function setupSparseCheckout(phrenPath, projects) {
|
|
|
115
116
|
execFileSync("git", ["rev-parse", "--git-dir"], { cwd: phrenPath, stdio: "ignore", timeout: EXEC_TIMEOUT_QUICK_MS });
|
|
116
117
|
}
|
|
117
118
|
catch (err) {
|
|
118
|
-
|
|
119
|
-
process.stderr.write(`[phren] setupSparseCheckout notAGitRepo: ${errorMessage(err)}\n`);
|
|
119
|
+
logger.debug("link", `setupSparseCheckout notAGitRepo: ${errorMessage(err)}`);
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
122
|
const alwaysInclude = ["profiles", "machines.yaml", "global", "scripts", "link.sh", "README.md", ".gitignore"];
|
|
@@ -268,8 +268,7 @@ function linkGlobal(phrenPath, tools) {
|
|
|
268
268
|
symlinkFile(globalClaude, path.join(copilotInstrDir, "copilot-instructions.md"), phrenPath);
|
|
269
269
|
}
|
|
270
270
|
catch (err) {
|
|
271
|
-
|
|
272
|
-
process.stderr.write(`[phren] linkGlobal copilotInstructions: ${errorMessage(err)}\n`);
|
|
271
|
+
logger.debug("link", `linkGlobal copilotInstructions: ${errorMessage(err)}`);
|
|
273
272
|
}
|
|
274
273
|
}
|
|
275
274
|
}
|
|
@@ -314,8 +313,7 @@ function linkProject(phrenPath, project, tools) {
|
|
|
314
313
|
symlinkFile(src, path.join(copilotDir, "copilot-instructions.md"), phrenPath);
|
|
315
314
|
}
|
|
316
315
|
catch (err) {
|
|
317
|
-
|
|
318
|
-
process.stderr.write(`[phren] linkProject copilotInstructions: ${errorMessage(err)}\n`);
|
|
316
|
+
logger.debug("link", `linkProject copilotInstructions: ${errorMessage(err)}`);
|
|
319
317
|
}
|
|
320
318
|
}
|
|
321
319
|
}
|
|
@@ -338,8 +336,7 @@ function linkProject(phrenPath, project, tools) {
|
|
|
338
336
|
addTokenAnnotation(claudeFile);
|
|
339
337
|
}
|
|
340
338
|
catch (err) {
|
|
341
|
-
|
|
342
|
-
process.stderr.write(`[phren] linkProject tokenAnnotation: ${errorMessage(err)}\n`);
|
|
339
|
+
logger.debug("link", `linkProject tokenAnnotation: ${errorMessage(err)}`);
|
|
343
340
|
}
|
|
344
341
|
}
|
|
345
342
|
// Project-level skills
|
|
@@ -355,8 +352,7 @@ function linkProject(phrenPath, project, tools) {
|
|
|
355
352
|
excludeEntries.push("AGENTS.md");
|
|
356
353
|
}
|
|
357
354
|
catch (err) {
|
|
358
|
-
|
|
359
|
-
process.stderr.write(`[phren] linkProject agentsMd: ${errorMessage(err)}\n`);
|
|
355
|
+
logger.debug("link", `linkProject agentsMd: ${errorMessage(err)}`);
|
|
360
356
|
}
|
|
361
357
|
}
|
|
362
358
|
// Auto-exclude phren-managed files from git status
|
|
@@ -507,8 +503,7 @@ export async function runLink(phrenPath, opts = {}) {
|
|
|
507
503
|
log(` phren.SKILL.md written (agentskills-compatible tools)`);
|
|
508
504
|
}
|
|
509
505
|
catch (err) {
|
|
510
|
-
|
|
511
|
-
process.stderr.write(`[phren] link writeSkillMd: ${errorMessage(err)}\n`);
|
|
506
|
+
logger.debug("link", `link writeSkillMd: ${errorMessage(err)}`);
|
|
512
507
|
}
|
|
513
508
|
log("");
|
|
514
509
|
// Step 7: Context file
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import * as yaml from "js-yaml";
|
|
4
|
-
import { debugLog } from "
|
|
5
|
-
import { errorMessage } from "
|
|
6
|
-
import { buildSharedLifecycleCommands } from "
|
|
7
|
-
import { VERSION } from "
|
|
8
|
-
import { getToolCount, renderToolCatalogMarkdown } from "
|
|
9
|
-
import { isSkillEnabled } from "
|
|
4
|
+
import { debugLog } from "../shared.js";
|
|
5
|
+
import { errorMessage } from "../utils.js";
|
|
6
|
+
import { buildSharedLifecycleCommands } from "../hooks.js";
|
|
7
|
+
import { VERSION } from "../package-metadata.js";
|
|
8
|
+
import { getToolCount, renderToolCatalogMarkdown } from "../tool-registry.js";
|
|
9
|
+
import { isSkillEnabled } from "../skill/state.js";
|
|
10
|
+
import { logger } from "../logger.js";
|
|
10
11
|
const REQUIRED_SKILL_FIELDS = ["name", "description"];
|
|
11
12
|
export function parseSkillFrontmatter(rawContent) {
|
|
12
13
|
// Normalize UTF-8 BOM and Windows-style CRLF line endings before matching frontmatter
|
|
@@ -190,8 +191,7 @@ function cleanupManagedSkillLinks(destDir, expectedNames, managedRoot) {
|
|
|
190
191
|
fs.unlinkSync(destPath);
|
|
191
192
|
}
|
|
192
193
|
catch (err) {
|
|
193
|
-
|
|
194
|
-
process.stderr.write(`[phren] cleanupManagedSkillLinks: ${errorMessage(err)}\n`);
|
|
194
|
+
logger.debug("link-skills", `cleanupManagedSkillLinks: ${errorMessage(err)}`);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
}
|
|
@@ -217,7 +217,7 @@ export function linkSkillsDir(srcDir, destDir, managedRoot, symlinkFile, opts) {
|
|
|
217
217
|
message: `Skipping skill '${skillName}' — user skill already exists at ${destPath}. To use phren's version, rename or remove your skill first.`,
|
|
218
218
|
};
|
|
219
219
|
collisions.push(collision);
|
|
220
|
-
|
|
220
|
+
logger.warn("link-skills", collision.message);
|
|
221
221
|
continue;
|
|
222
222
|
}
|
|
223
223
|
expectedNames.add(entry);
|
|
@@ -234,7 +234,7 @@ export function linkSkillsDir(srcDir, destDir, managedRoot, symlinkFile, opts) {
|
|
|
234
234
|
message: `Skipping skill '${skillName}' — user skill already exists at ${destPath}. To use phren's version, rename or remove your skill first.`,
|
|
235
235
|
};
|
|
236
236
|
collisions.push(collision);
|
|
237
|
-
|
|
237
|
+
logger.warn("link-skills", collision.message);
|
|
238
238
|
continue;
|
|
239
239
|
}
|
|
240
240
|
expectedNames.add(entry);
|
package/mcp/dist/logger.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import { runtimeFile, findPhrenPath } from "./shared.js";
|
|
3
|
+
let _cachedPhrenPath;
|
|
3
4
|
export function log(level, tool, message, extra) {
|
|
4
5
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
if (_cachedPhrenPath === undefined)
|
|
7
|
+
_cachedPhrenPath = findPhrenPath();
|
|
8
|
+
if (!_cachedPhrenPath)
|
|
7
9
|
return;
|
|
8
|
-
const logPath = runtimeFile(
|
|
10
|
+
const logPath = runtimeFile(_cachedPhrenPath, "debug.log");
|
|
9
11
|
const line = JSON.stringify({ ts: new Date().toISOString(), level, tool, message, ...extra });
|
|
10
12
|
fs.appendFileSync(logPath, line + "\n");
|
|
11
13
|
}
|
|
@@ -13,3 +15,9 @@ export function log(level, tool, message, extra) {
|
|
|
13
15
|
// Logging must never throw
|
|
14
16
|
}
|
|
15
17
|
}
|
|
18
|
+
export const logger = {
|
|
19
|
+
debug: (tool, message, extra) => log("debug", tool, message, extra),
|
|
20
|
+
info: (tool, message, extra) => log("info", tool, message, extra),
|
|
21
|
+
warn: (tool, message, extra) => log("warn", tool, message, extra),
|
|
22
|
+
error: (tool, message, extra) => log("error", tool, message, extra),
|
|
23
|
+
};
|
|
@@ -3,7 +3,7 @@ import * as path from "path";
|
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
5
|
export const ROOT = path.join(__dirname, "..", "..");
|
|
6
|
-
|
|
6
|
+
const PACKAGE_JSON_PATH = path.join(ROOT, "package.json");
|
|
7
7
|
function readPackageJson() {
|
|
8
8
|
return JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, "utf8"));
|
|
9
9
|
}
|
package/mcp/dist/phren-art.js
CHANGED
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Phren character ASCII/Unicode art
|
|
2
|
+
* Phren character ASCII/Unicode art for CLI presence.
|
|
3
3
|
*
|
|
4
4
|
* Based on the pixel art: purple 8-bit brain with diamond eyes,
|
|
5
5
|
* smile, little legs, and cyan sparkle.
|
|
6
|
-
*
|
|
7
|
-
* Animation system provides lifelike movement through composable effects:
|
|
8
|
-
* bob, blink, sparkle, and lean — all driven by setTimeout with randomized
|
|
9
|
-
* intervals for organic timing.
|
|
10
6
|
*/
|
|
11
|
-
const ESC = "\x1b[";
|
|
12
|
-
const RESET = `${ESC}0m`;
|
|
13
|
-
const PURPLE = `${ESC}35m`; // magenta — body
|
|
14
|
-
const BRIGHT_PURPLE = `${ESC}95m`; // bright magenta — highlights
|
|
15
|
-
const CYAN = `${ESC}96m`; // bright cyan — sparkle
|
|
16
|
-
const DIM = `${ESC}2m`;
|
|
17
|
-
const DARK_PURPLE = `${ESC}38;5;57m`; // deep purple — shadow/outline
|
|
18
|
-
const LIGHT_PURPLE = `${ESC}38;5;141m`; // lavender — brain highlights
|
|
19
|
-
const MID_PURPLE = `${ESC}38;5;98m`; // mid tone
|
|
20
|
-
const NAVY = `${ESC}38;5;18m`; // darkest outline
|
|
21
|
-
// ── Art constants (24px wide, truecolor half-blocks) ─────────────────────────
|
|
22
7
|
/**
|
|
23
8
|
* Phren truecolor art (24px wide, generated from phren-transparent.png).
|
|
24
9
|
* Uses half-block ▀ with RGB foreground+background for pixel-faithful rendering.
|
|
@@ -38,35 +23,19 @@ export const PHREN_ART = [
|
|
|
38
23
|
" ",
|
|
39
24
|
" ",
|
|
40
25
|
];
|
|
41
|
-
/** The art width in visible columns */
|
|
42
|
-
const ART_WIDTH = 24;
|
|
43
26
|
// ── Sparkle row: the cyan pixels at row 2 ────────────────────────────────────
|
|
44
|
-
// Sparkle uses ▄ half-blocks with cyan truecolor. For animation we cycle through
|
|
45
|
-
// decorative unicode characters at different brightness levels.
|
|
46
27
|
const SPARKLE_ROW = 2;
|
|
47
|
-
const SPARKLE_CHARS = ["\u2726", "\u2727", "\u2736", " "];
|
|
48
|
-
// ── Eye detection: dark navy pixels in row 6
|
|
49
|
-
// Row 6 has two eye pixels at segments 1 and 5 (visual positions 7 and 11).
|
|
50
|
-
// When blinking, we replace their dark fg color with the surrounding body purple.
|
|
28
|
+
const SPARKLE_CHARS = ["\u2726", "\u2727", "\u2736", " "];
|
|
29
|
+
// ── Eye detection: dark navy pixels in row 6 ─────────────────────────────────
|
|
51
30
|
const EYE_ROW = 6;
|
|
52
|
-
// Dark-pixel fg threshold
|
|
53
31
|
const EYE_R_MAX = 30;
|
|
54
32
|
const EYE_G_MAX = 45;
|
|
55
33
|
const EYE_B_MAX = 120;
|
|
56
|
-
// Body-purple color to use when "closing" eyes (average of surrounding pixels)
|
|
57
34
|
const BLINK_COLOR = "146;130;250";
|
|
58
|
-
/**
|
|
59
|
-
* Flip a single art line horizontally. Reverses the order of colored pixel
|
|
60
|
-
* segments and swaps leading/trailing whitespace so the character faces right.
|
|
61
|
-
* Half-block characters (▀ ▄) are horizontally symmetric so no char swap needed.
|
|
62
|
-
*/
|
|
63
35
|
function flipLine(line) {
|
|
64
36
|
const stripped = line.replace(/\x1b\[[^m]*m/g, "");
|
|
65
37
|
const leadSpaces = stripped.match(/^( *)/)[1].length;
|
|
66
38
|
const trailSpaces = stripped.match(/( *)$/)[1].length;
|
|
67
|
-
// Parse pixel segments: each is one or two ANSI color codes followed by a block char.
|
|
68
|
-
// We strip any leading reset (\x1b[0m) from captured codes — it's an artifact from
|
|
69
|
-
// the original per-pixel reset pattern and will be re-added during reassembly.
|
|
70
39
|
const pixels = [];
|
|
71
40
|
const pixelRegex = /((?:\x1b\[[^m]*m)+)([\u2580\u2584])/g;
|
|
72
41
|
let match;
|
|
@@ -76,7 +45,7 @@ function flipLine(line) {
|
|
|
76
45
|
pixels.push({ codes, char: match[2] });
|
|
77
46
|
}
|
|
78
47
|
if (pixels.length === 0)
|
|
79
|
-
return line;
|
|
48
|
+
return line;
|
|
80
49
|
const reversed = [...pixels].reverse();
|
|
81
50
|
const newLead = " ".repeat(trailSpaces);
|
|
82
51
|
const newTrail = " ".repeat(leadSpaces);
|
|
@@ -87,26 +56,14 @@ function flipLine(line) {
|
|
|
87
56
|
result += newTrail;
|
|
88
57
|
return result;
|
|
89
58
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Generate horizontally flipped art (facing right).
|
|
92
|
-
*/
|
|
93
59
|
function generateFlippedArt(art) {
|
|
94
60
|
return art.map(flipLine);
|
|
95
61
|
}
|
|
96
|
-
/** Pre-computed right-facing art */
|
|
97
62
|
export const PHREN_ART_RIGHT = generateFlippedArt(PHREN_ART);
|
|
98
|
-
/** Random integer in [min, max] inclusive */
|
|
99
63
|
function randInt(min, max) {
|
|
100
64
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
101
65
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Replace eye pixels on a single line with the blink color.
|
|
104
|
-
* Eye pixels are identified by their dark navy fg color (R<30, G<45, B<120).
|
|
105
|
-
* We replace the fg color code while preserving the bg code and character.
|
|
106
|
-
*/
|
|
107
66
|
function applyBlinkToLine(line) {
|
|
108
|
-
// Match ANSI color sequences: each pixel is \e[38;2;R;G;Bm (optionally with \e[48;2;...m) then a block char
|
|
109
|
-
// We scan for fg codes that match the eye threshold and replace them with the blink color
|
|
110
67
|
return line.replace(/\x1b\[38;2;(\d+);(\d+);(\d+)m/g, (full, rStr, gStr, bStr) => {
|
|
111
68
|
const r = Number(rStr);
|
|
112
69
|
const g = Number(gStr);
|
|
@@ -117,44 +74,24 @@ function applyBlinkToLine(line) {
|
|
|
117
74
|
return full;
|
|
118
75
|
});
|
|
119
76
|
}
|
|
120
|
-
/**
|
|
121
|
-
* Replace the sparkle pixels on the sparkle row with the current sparkle character.
|
|
122
|
-
* The sparkle row has two cyan ▄ half-blocks. During sparkle animation we replace
|
|
123
|
-
* them with decorative Unicode characters from SPARKLE_CHARS.
|
|
124
|
-
*/
|
|
125
77
|
function applySparkleToLine(line, frame, active) {
|
|
126
78
|
if (!active)
|
|
127
79
|
return line;
|
|
128
80
|
const sparkleChar = SPARKLE_CHARS[frame % SPARKLE_CHARS.length];
|
|
129
81
|
if (sparkleChar === " ") {
|
|
130
|
-
// Dim: replace the cyan-colored segments with spaces (they disappear)
|
|
131
82
|
return line.replace(/\x1b\[38;2;\d+;2\d\d;2\d\dm[\u2580\u2584]\x1b\[0m/g, " ");
|
|
132
83
|
}
|
|
133
|
-
// Replace the half-block characters with the sparkle unicode char, keeping the cyan color
|
|
134
84
|
return line.replace(/(\x1b\[38;2;\d+;2\d\d;2\d\dm)[\u2580\u2584](\x1b\[0m)/g, `$1${sparkleChar}$2`);
|
|
135
85
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Apply lean (horizontal shift) to a line.
|
|
138
|
-
* Positive offset = shift right (prepend spaces, trim from end).
|
|
139
|
-
* Negative offset = shift left (trim from start, append spaces).
|
|
140
|
-
*/
|
|
141
86
|
function applyLean(line, offset) {
|
|
142
87
|
if (offset === 0)
|
|
143
88
|
return line;
|
|
144
89
|
if (offset > 0) {
|
|
145
|
-
// Shift right: prepend spaces
|
|
146
90
|
return " ".repeat(offset) + line;
|
|
147
91
|
}
|
|
148
|
-
// Shift left: remove leading spaces (up to |offset|)
|
|
149
92
|
const trimCount = Math.min(-offset, line.match(/^( *)/)[1].length);
|
|
150
93
|
return line.slice(trimCount);
|
|
151
94
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Create an animated phren character controller.
|
|
154
|
-
*
|
|
155
|
-
* @param options.facing - 'left' (default, original) or 'right' (flipped)
|
|
156
|
-
* @param options.size - art width; unused but reserved for future scaling (default 24)
|
|
157
|
-
*/
|
|
158
95
|
export function createPhrenAnimator(options) {
|
|
159
96
|
const facing = options?.facing ?? "left";
|
|
160
97
|
const baseArt = facing === "right" ? PHREN_ART_RIGHT : PHREN_ART;
|
|
@@ -170,22 +107,18 @@ export function createPhrenAnimator(options) {
|
|
|
170
107
|
const t = setTimeout(fn, ms);
|
|
171
108
|
timers.push(t);
|
|
172
109
|
}
|
|
173
|
-
// ── Bob animation: toggles bobUp every ~500ms ──────────────────────────
|
|
174
110
|
function scheduleBob() {
|
|
175
111
|
scheduleTimer(() => {
|
|
176
112
|
state.bobUp = !state.bobUp;
|
|
177
113
|
scheduleBob();
|
|
178
114
|
}, 500);
|
|
179
115
|
}
|
|
180
|
-
// ── Blink animation: eyes close for 150ms, random 2-8s intervals ──────
|
|
181
116
|
function scheduleBlink() {
|
|
182
117
|
const interval = randInt(2000, 8000);
|
|
183
118
|
scheduleTimer(() => {
|
|
184
|
-
// Perform blink
|
|
185
119
|
state.isBlinking = true;
|
|
186
120
|
scheduleTimer(() => {
|
|
187
121
|
state.isBlinking = false;
|
|
188
|
-
// 30% chance of double-blink
|
|
189
122
|
if (Math.random() < 0.3) {
|
|
190
123
|
scheduleTimer(() => {
|
|
191
124
|
state.isBlinking = true;
|
|
@@ -201,9 +134,7 @@ export function createPhrenAnimator(options) {
|
|
|
201
134
|
}, 150);
|
|
202
135
|
}, interval);
|
|
203
136
|
}
|
|
204
|
-
// ── Sparkle animation: fast cycle during bursts, long pauses between ───
|
|
205
137
|
function scheduleSparkle() {
|
|
206
|
-
// Wait 1-5 seconds before next sparkle burst
|
|
207
138
|
const pause = randInt(1000, 5000);
|
|
208
139
|
scheduleTimer(() => {
|
|
209
140
|
state.sparkleActive = true;
|
|
@@ -213,7 +144,6 @@ export function createPhrenAnimator(options) {
|
|
|
213
144
|
}
|
|
214
145
|
function sparkleStep(step) {
|
|
215
146
|
if (step >= SPARKLE_CHARS.length) {
|
|
216
|
-
// Burst complete
|
|
217
147
|
state.sparkleActive = false;
|
|
218
148
|
scheduleSparkle();
|
|
219
149
|
return;
|
|
@@ -223,7 +153,6 @@ export function createPhrenAnimator(options) {
|
|
|
223
153
|
sparkleStep(step + 1);
|
|
224
154
|
}, 200);
|
|
225
155
|
}
|
|
226
|
-
// ── Lean animation: shift 1 col left or right every 4-10s, hold 1-2s ──
|
|
227
156
|
function scheduleLean() {
|
|
228
157
|
const interval = randInt(4000, 10000);
|
|
229
158
|
scheduleTimer(() => {
|
|
@@ -240,19 +169,15 @@ export function createPhrenAnimator(options) {
|
|
|
240
169
|
getFrame() {
|
|
241
170
|
let lines = baseArt.map((line, i) => {
|
|
242
171
|
let result = line;
|
|
243
|
-
// Apply blink to the eye row
|
|
244
172
|
if (state.isBlinking && i === EYE_ROW) {
|
|
245
173
|
result = applyBlinkToLine(result);
|
|
246
174
|
}
|
|
247
|
-
// Apply sparkle to sparkle row
|
|
248
175
|
if (i === SPARKLE_ROW) {
|
|
249
176
|
result = applySparkleToLine(result, state.sparkleFrame, state.sparkleActive);
|
|
250
177
|
}
|
|
251
|
-
// Apply lean
|
|
252
178
|
result = applyLean(result, state.leanOffset);
|
|
253
179
|
return result;
|
|
254
180
|
});
|
|
255
|
-
// Apply bob: when bobUp, prepend a blank line (shift everything down visually)
|
|
256
181
|
if (state.bobUp) {
|
|
257
182
|
lines = ["", ...lines.slice(0, -1)];
|
|
258
183
|
}
|
|
@@ -272,56 +197,9 @@ export function createPhrenAnimator(options) {
|
|
|
272
197
|
},
|
|
273
198
|
};
|
|
274
199
|
}
|
|
275
|
-
// ── Startup frames (pre-baked, no timers) ────────────────────────────────────
|
|
276
|
-
/**
|
|
277
|
-
* Returns 4 pre-baked animation frames for shell startup display.
|
|
278
|
-
* No timers needed — the caller cycles through them manually.
|
|
279
|
-
*
|
|
280
|
-
* Frames: [neutral, bob-up, neutral, bob-down(sparkle)]
|
|
281
|
-
*
|
|
282
|
-
* @param facing - 'left' (default) or 'right'
|
|
283
|
-
*/
|
|
284
|
-
export function getPhrenStartupFrames(facing) {
|
|
285
|
-
const art = facing === "right" ? PHREN_ART_RIGHT : PHREN_ART;
|
|
286
|
-
// Frame 0: neutral
|
|
287
|
-
const frame0 = [...art];
|
|
288
|
-
// Frame 1: bob up (prepend blank line, drop last line)
|
|
289
|
-
const frame1 = ["", ...art.slice(0, -1)];
|
|
290
|
-
// Frame 2: neutral (same as frame 0)
|
|
291
|
-
const frame2 = [...art];
|
|
292
|
-
// Frame 3: bob down with sparkle burst — shift down by removing first line, append blank
|
|
293
|
-
const frame3WithSparkle = art.map((line, i) => {
|
|
294
|
-
if (i === SPARKLE_ROW) {
|
|
295
|
-
return applySparkleToLine(line, 0, true); // ✦ sparkle
|
|
296
|
-
}
|
|
297
|
-
return line;
|
|
298
|
-
});
|
|
299
|
-
const frame3 = [...frame3WithSparkle.slice(1), ""];
|
|
300
|
-
return [frame0, frame1, frame2, frame3];
|
|
301
|
-
}
|
|
302
|
-
// ── Legacy exports (unchanged) ───────────────────────────────────────────────
|
|
303
|
-
/** Single-line compact phren for inline use */
|
|
304
|
-
export const PHREN_INLINE = `${PURPLE}◆${RESET}`;
|
|
305
|
-
/** Phren spinner frames for search/sync operations — cycles through in purple */
|
|
306
|
-
export const PHREN_SPINNER_FRAMES = [
|
|
307
|
-
`${BRIGHT_PURPLE}◆${RESET}`,
|
|
308
|
-
`${PURPLE}◇${RESET}`,
|
|
309
|
-
`${CYAN}✦${RESET}`,
|
|
310
|
-
`${PURPLE}✧${RESET}`,
|
|
311
|
-
`${BRIGHT_PURPLE}◆${RESET}`,
|
|
312
|
-
`${DARK_PURPLE}◇${RESET}`,
|
|
313
|
-
];
|
|
314
|
-
/** Default spinner interval in ms */
|
|
315
|
-
export const PHREN_SPINNER_INTERVAL_MS = 120;
|
|
316
200
|
/**
|
|
317
201
|
* Return the phren art as a single string, optionally indented.
|
|
318
202
|
*/
|
|
319
203
|
export function renderPhrenArt(indent = "") {
|
|
320
204
|
return PHREN_ART.map(line => indent + line).join("\n");
|
|
321
205
|
}
|
|
322
|
-
/**
|
|
323
|
-
* Get a spinner frame by index (wraps around automatically).
|
|
324
|
-
*/
|
|
325
|
-
export function spinnerFrame(tick) {
|
|
326
|
-
return PHREN_SPINNER_FRAMES[tick % PHREN_SPINNER_FRAMES.length];
|
|
327
|
-
}
|
package/mcp/dist/phren-paths.js
CHANGED
|
@@ -7,6 +7,10 @@ import { bootstrapPhrenDotEnv } from "./phren-dotenv.js";
|
|
|
7
7
|
import { PhrenError, isRecord, RESERVED_PROJECT_DIR_NAMES } from "./phren-core.js";
|
|
8
8
|
import { errorMessage, isValidProjectName, safeProjectPath } from "./utils.js";
|
|
9
9
|
bootstrapPhrenDotEnv();
|
|
10
|
+
function stderrLog(msg) { try {
|
|
11
|
+
process.stderr.write("[phren] " + msg + "\n");
|
|
12
|
+
}
|
|
13
|
+
catch { } }
|
|
10
14
|
export const ROOT_MANIFEST_FILENAME = "phren.root.yaml";
|
|
11
15
|
export function homeDir() {
|
|
12
16
|
return process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
@@ -75,7 +79,7 @@ export function readRootManifest(phrenPath) {
|
|
|
75
79
|
}
|
|
76
80
|
catch (err) {
|
|
77
81
|
if ((process.env.PHREN_DEBUG))
|
|
78
|
-
|
|
82
|
+
stderrLog(`readRootManifest: ${errorMessage(err)}`);
|
|
79
83
|
return null;
|
|
80
84
|
}
|
|
81
85
|
}
|
|
@@ -263,6 +267,20 @@ export function debugLog(msg) {
|
|
|
263
267
|
// debug log is best-effort; logging errors about logging would recurse
|
|
264
268
|
}
|
|
265
269
|
}
|
|
270
|
+
/** Always-on structured error log (no PHREN_DEBUG gate). */
|
|
271
|
+
export function errorLog(tool, msg) {
|
|
272
|
+
try {
|
|
273
|
+
const phrenPath = findPhrenPath();
|
|
274
|
+
if (!phrenPath)
|
|
275
|
+
return;
|
|
276
|
+
const logFile = runtimeFile(phrenPath, "debug.log");
|
|
277
|
+
const line = JSON.stringify({ ts: new Date().toISOString(), level: "error", tool, message: msg });
|
|
278
|
+
fs.appendFileSync(logFile, line + "\n");
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
// Logging must never throw
|
|
282
|
+
}
|
|
283
|
+
}
|
|
266
284
|
export function appendIndexEvent(phrenPath, event) {
|
|
267
285
|
try {
|
|
268
286
|
const file = runtimeFile(phrenPath, "index-events.jsonl");
|
|
@@ -270,7 +288,7 @@ export function appendIndexEvent(phrenPath, event) {
|
|
|
270
288
|
}
|
|
271
289
|
catch (err) {
|
|
272
290
|
if ((process.env.PHREN_DEBUG))
|
|
273
|
-
|
|
291
|
+
stderrLog(`appendIndexEvent: ${errorMessage(err)}`);
|
|
274
292
|
}
|
|
275
293
|
}
|
|
276
294
|
/** Resolve the canonical findings file for a project directory. */
|
|
@@ -301,7 +319,7 @@ export function findProjectNameCaseInsensitive(phrenPath, name) {
|
|
|
301
319
|
}
|
|
302
320
|
catch (err) {
|
|
303
321
|
if ((process.env.PHREN_DEBUG))
|
|
304
|
-
|
|
322
|
+
stderrLog(`findProjectNameCaseInsensitive: ${errorMessage(err)}`);
|
|
305
323
|
}
|
|
306
324
|
return null;
|
|
307
325
|
}
|
|
@@ -318,7 +336,7 @@ export function findArchivedProjectNameCaseInsensitive(phrenPath, name) {
|
|
|
318
336
|
}
|
|
319
337
|
catch (err) {
|
|
320
338
|
if ((process.env.PHREN_DEBUG))
|
|
321
|
-
|
|
339
|
+
stderrLog(`findArchivedProjectNameCaseInsensitive: ${errorMessage(err)}`);
|
|
322
340
|
}
|
|
323
341
|
return null;
|
|
324
342
|
}
|
|
@@ -342,26 +360,26 @@ export function getProjectDirs(phrenPath, profile) {
|
|
|
342
360
|
}
|
|
343
361
|
if (profile) {
|
|
344
362
|
if (!isValidProjectName(profile)) {
|
|
345
|
-
|
|
363
|
+
errorLog("getProjectDirs", `${PhrenError.VALIDATION_ERROR}: Invalid PHREN_PROFILE value: ${profile}`);
|
|
346
364
|
return [];
|
|
347
365
|
}
|
|
348
366
|
const profilePath = path.join(phrenPath, "profiles", `${profile}.yaml`);
|
|
349
367
|
if (!fs.existsSync(profilePath)) {
|
|
350
|
-
|
|
368
|
+
errorLog("getProjectDirs", `${PhrenError.FILE_NOT_FOUND}: Profile file not found: ${profilePath}`);
|
|
351
369
|
return [];
|
|
352
370
|
}
|
|
353
371
|
try {
|
|
354
372
|
const data = yaml.load(fs.readFileSync(profilePath, "utf-8"), { schema: yaml.CORE_SCHEMA });
|
|
355
373
|
const projects = isRecord(data) ? data.projects : undefined;
|
|
356
374
|
if (!Array.isArray(projects)) {
|
|
357
|
-
|
|
375
|
+
errorLog("getProjectDirs", `${PhrenError.MALFORMED_YAML}: Profile YAML missing valid "projects" array: ${profilePath}`);
|
|
358
376
|
return [];
|
|
359
377
|
}
|
|
360
378
|
const listed = projects
|
|
361
379
|
.map((p) => {
|
|
362
380
|
const name = String(p);
|
|
363
381
|
if (!isValidProjectName(name)) {
|
|
364
|
-
|
|
382
|
+
errorLog("getProjectDirs", `${PhrenError.VALIDATION_ERROR}: Skipping invalid project name in profile: ${name}`);
|
|
365
383
|
return null;
|
|
366
384
|
}
|
|
367
385
|
return safeProjectPath(phrenPath, name);
|
|
@@ -374,8 +392,8 @@ export function getProjectDirs(phrenPath, profile) {
|
|
|
374
392
|
}
|
|
375
393
|
catch (err) {
|
|
376
394
|
if ((process.env.PHREN_DEBUG))
|
|
377
|
-
|
|
378
|
-
|
|
395
|
+
stderrLog(`getProjectDirs yamlParse: ${errorMessage(err)}`);
|
|
396
|
+
errorLog("getProjectDirs", `${PhrenError.MALFORMED_YAML}: Malformed profile YAML: ${profilePath}`);
|
|
379
397
|
return [];
|
|
380
398
|
}
|
|
381
399
|
}
|
|
@@ -386,7 +404,7 @@ export function getProjectDirs(phrenPath, profile) {
|
|
|
386
404
|
}
|
|
387
405
|
catch (err) {
|
|
388
406
|
if ((process.env.PHREN_DEBUG))
|
|
389
|
-
|
|
407
|
+
stderrLog(`getProjectDirs: ${errorMessage(err)}`);
|
|
390
408
|
return [];
|
|
391
409
|
}
|
|
392
410
|
}
|
|
@@ -413,7 +431,7 @@ export function collectNativeMemoryFiles() {
|
|
|
413
431
|
}
|
|
414
432
|
catch (err) {
|
|
415
433
|
if ((process.env.PHREN_DEBUG))
|
|
416
|
-
|
|
434
|
+
stderrLog(`collectNativeMemoryFiles: ${errorMessage(err)}`);
|
|
417
435
|
}
|
|
418
436
|
return results;
|
|
419
437
|
}
|