@phren/cli 0.0.38 → 0.0.39

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.
@@ -24,10 +24,33 @@ function acquireFileLock(lockPath) {
24
24
  try {
25
25
  const stat = fs.statSync(lockPath);
26
26
  if (Date.now() - stat.mtimeMs > staleThreshold) {
27
- // Lock file is older than stale threshold — delete unconditionally.
28
- // This handles zombie processes, crashed hooks, and any case where
29
- // the owning process failed to clean up.
30
- fs.unlinkSync(lockPath);
27
+ // Lock mtime exceeds stale threshold — but only delete if the
28
+ // owning process is dead. Legitimate long operations (e.g. index
29
+ // rebuilds) can hold locks beyond the threshold.
30
+ let ownerAlive = false;
31
+ try {
32
+ const content = fs.readFileSync(lockPath, "utf-8");
33
+ const pid = Number.parseInt(content.split("\n")[0], 10);
34
+ if (pid > 0 && Number.isFinite(pid)) {
35
+ try {
36
+ process.kill(pid, 0); // signal 0: liveness check, no actual signal sent
37
+ ownerAlive = true;
38
+ }
39
+ catch {
40
+ // process.kill throws if PID doesn't exist → owner is dead
41
+ }
42
+ }
43
+ // pid <= 0 or NaN → treat as stale (unparseable / corrupt lock)
44
+ }
45
+ catch {
46
+ // Can't read lock file (deleted between stat and read) → retry
47
+ }
48
+ if (!ownerAlive) {
49
+ try {
50
+ fs.unlinkSync(lockPath);
51
+ }
52
+ catch { /* already gone */ }
53
+ }
31
54
  continue;
32
55
  }
33
56
  }
@@ -6,8 +6,7 @@ import { errorMessage } from "../utils.js";
6
6
  import { configureAllHooks } from "../hooks.js";
7
7
  import { configureClaude } from "./config.js";
8
8
  import { getMcpEnabledPreference, getHooksEnabledPreference, setHooksEnabledPreference, } from "./preferences.js";
9
- import { DEFAULT_PHREN_PATH, log } from "./shared.js";
10
- import { parseMcpMode } from "./init.js";
9
+ import { DEFAULT_PHREN_PATH, log, parseMcpMode } from "./shared.js";
11
10
  export async function runHooksMode(modeArg) {
12
11
  const phrenPath = findPhrenPath() || (process.env.PHREN_PATH) || DEFAULT_PHREN_PATH;
13
12
  const manifest = readRootManifest(phrenPath);
@@ -5,8 +5,7 @@ import { debugLog, findPhrenPath, readRootManifest } from "../shared.js";
5
5
  import { errorMessage } from "../utils.js";
6
6
  import { configureClaude, configureVSCode, configureCursorMcp, configureCopilotMcp, configureCodexMcp, } from "./config.js";
7
7
  import { getMcpEnabledPreference, getHooksEnabledPreference, setMcpEnabledPreference, } from "./preferences.js";
8
- import { DEFAULT_PHREN_PATH, log } from "./shared.js";
9
- import { parseMcpMode } from "./init.js";
8
+ import { DEFAULT_PHREN_PATH, log, parseMcpMode } from "./shared.js";
10
9
  export async function runMcpMode(modeArg) {
11
10
  const phrenPath = findPhrenPath() || (process.env.PHREN_PATH) || DEFAULT_PHREN_PATH;
12
11
  const manifest = readRootManifest(phrenPath);
@@ -29,6 +29,7 @@ import { DEFAULT_PHREN_PATH, STARTER_DIR, VERSION, log, confirmPrompt } from "./
29
29
  import { PROJECT_OWNERSHIP_MODES, getProjectOwnershipDefault, } from "../project-config.js";
30
30
  import { getWorkflowPolicy } from "../shared/governance.js";
31
31
  import { addProjectToProfile } from "../profile-store.js";
32
+ export { parseMcpMode } from "./shared.js";
32
33
  function parseVersion(version) {
33
34
  const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?/);
34
35
  if (!match)
@@ -63,14 +64,6 @@ export function isVersionNewer(current, previous) {
63
64
  return true;
64
65
  return c.pre > p.pre;
65
66
  }
66
- export function parseMcpMode(raw) {
67
- if (!raw)
68
- return undefined;
69
- const normalized = raw.trim().toLowerCase();
70
- if (normalized === "on" || normalized === "off")
71
- return normalized;
72
- return undefined;
73
- }
74
67
  function normalizedBootstrapProjectName(projectPath) {
75
68
  return path.basename(projectPath).toLowerCase().replace(/[^a-z0-9_-]/g, "-");
76
69
  }
@@ -67,6 +67,14 @@ export function nearestWritableTarget(filePath) {
67
67
  return false;
68
68
  }
69
69
  }
70
+ export function parseMcpMode(raw) {
71
+ if (!raw)
72
+ return undefined;
73
+ const normalized = raw.trim().toLowerCase();
74
+ if (normalized === "on" || normalized === "off")
75
+ return normalized;
76
+ return undefined;
77
+ }
70
78
  export async function confirmPrompt(message) {
71
79
  if (process.env.CI === "true" || !process.stdin.isTTY)
72
80
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phren/cli",
3
- "version": "0.0.38",
3
+ "version": "0.0.39",
4
4
  "description": "Knowledge layer for AI agents. Phren learns and recalls.",
5
5
  "type": "module",
6
6
  "bin": {