@phren/cli 0.0.33 → 0.0.35
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/hooks.js +41 -0
- package/mcp/dist/init/init.js +11 -3
- package/mcp/dist/init/setup.js +20 -0
- package/mcp/dist/link/doctor.js +9 -0
- package/package.json +1 -1
package/mcp/dist/hooks.js
CHANGED
|
@@ -200,6 +200,47 @@ exit $status
|
|
|
200
200
|
return false;
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Install a lightweight `phren` CLI wrapper at ~/.local/bin/phren so the bare
|
|
205
|
+
* `phren` command works without a global npm install. The wrapper simply execs
|
|
206
|
+
* `node <entry_script> "$@"`.
|
|
207
|
+
*/
|
|
208
|
+
export function installPhrenCliWrapper(phrenPath) {
|
|
209
|
+
const entry = resolveCliEntryScript();
|
|
210
|
+
if (!entry)
|
|
211
|
+
return false;
|
|
212
|
+
const localBinDir = homePath(".local", "bin");
|
|
213
|
+
const wrapperPath = path.join(localBinDir, "phren");
|
|
214
|
+
// Don't overwrite a real global install — only our own wrapper
|
|
215
|
+
if (fs.existsSync(wrapperPath)) {
|
|
216
|
+
try {
|
|
217
|
+
const existing = fs.readFileSync(wrapperPath, "utf8");
|
|
218
|
+
if (!existing.includes("PHREN_CLI_WRAPPER"))
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// File exists but unreadable — don't overwrite, could be a real binary
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const content = `#!/bin/sh
|
|
227
|
+
# PHREN_CLI_WRAPPER — managed by phren init; safe to delete
|
|
228
|
+
set -u
|
|
229
|
+
PHREN_PATH="\${PHREN_PATH:-${phrenPath}}"
|
|
230
|
+
export PHREN_PATH
|
|
231
|
+
exec node ${shellEscape(entry)} "$@"
|
|
232
|
+
`;
|
|
233
|
+
try {
|
|
234
|
+
fs.mkdirSync(localBinDir, { recursive: true });
|
|
235
|
+
atomicWriteText(wrapperPath, content);
|
|
236
|
+
fs.chmodSync(wrapperPath, 0o755);
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
debugLog(`installPhrenCliWrapper: failed: ${errorMessage(err)}`);
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
203
244
|
function validateCopilotConfig(config) {
|
|
204
245
|
return (typeof config.version === "number" &&
|
|
205
246
|
Array.isArray(config.hooks?.sessionStart) &&
|
package/mcp/dist/init/init.js
CHANGED
|
@@ -6,7 +6,7 @@ import * as fs from "fs";
|
|
|
6
6
|
import * as path from "path";
|
|
7
7
|
import * as crypto from "crypto";
|
|
8
8
|
import { execFileSync, spawnSync } from "child_process";
|
|
9
|
-
import { configureAllHooks } from "../hooks.js";
|
|
9
|
+
import { configureAllHooks, installPhrenCliWrapper } from "../hooks.js";
|
|
10
10
|
import { getMachineName, machineFilePath, persistMachineName } from "../machine-identity.js";
|
|
11
11
|
import { atomicWriteText, debugLog, isRecord, hookConfigPath, homeDir, homePath, expandHomePath, findPhrenPath, getProjectDirs, readRootManifest, writeRootManifest, } from "../shared.js";
|
|
12
12
|
import { isValidProjectName, errorMessage } from "../utils.js";
|
|
@@ -969,6 +969,14 @@ function configureHooksIfEnabled(phrenPath, hooksEnabled, verb) {
|
|
|
969
969
|
else {
|
|
970
970
|
log(` Hooks are disabled by preference (run: npx phren hooks-mode on)`);
|
|
971
971
|
}
|
|
972
|
+
// Install phren CLI wrapper at ~/.local/bin/phren so the bare command works
|
|
973
|
+
const wrapperInstalled = installPhrenCliWrapper(phrenPath);
|
|
974
|
+
if (wrapperInstalled) {
|
|
975
|
+
log(` ${verb} CLI wrapper: ~/.local/bin/phren`);
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
log(` Note: phren CLI wrapper not installed (existing non-managed binary, or no entry script found)`);
|
|
979
|
+
}
|
|
972
980
|
}
|
|
973
981
|
export async function runInit(opts = {}) {
|
|
974
982
|
if ((opts.mode || "shared") === "project-local") {
|
|
@@ -1956,9 +1964,9 @@ export async function runUninstall(opts = {}) {
|
|
|
1956
1964
|
catch (err) {
|
|
1957
1965
|
debugLog(`uninstall: cleanup failed for ${codexHooksFile}: ${errorMessage(err)}`);
|
|
1958
1966
|
}
|
|
1959
|
-
// Remove session wrapper scripts (written by installSessionWrapper)
|
|
1967
|
+
// Remove session wrapper scripts (written by installSessionWrapper) and CLI wrapper
|
|
1960
1968
|
const localBinDir = path.join(home, ".local", "bin");
|
|
1961
|
-
for (const tool of ["copilot", "cursor", "codex"]) {
|
|
1969
|
+
for (const tool of ["copilot", "cursor", "codex", "phren"]) {
|
|
1962
1970
|
const wrapperPath = path.join(localBinDir, tool);
|
|
1963
1971
|
try {
|
|
1964
1972
|
if (fs.existsSync(wrapperPath)) {
|
package/mcp/dist/init/setup.js
CHANGED
|
@@ -6,6 +6,7 @@ import * as path from "path";
|
|
|
6
6
|
import * as os from "os";
|
|
7
7
|
import * as yaml from "js-yaml";
|
|
8
8
|
import { atomicWriteText, debugLog, findProjectNameCaseInsensitive, hookConfigPath, EXEC_TIMEOUT_QUICK_MS, readRootManifest, sessionsDir, runtimeHealthFile, isRecord, } from "../shared.js";
|
|
9
|
+
import { homePath } from "../phren-paths.js";
|
|
9
10
|
import { addProjectToProfile, listProfiles, resolveActiveProfile, setMachineProfile } from "../profile-store.js";
|
|
10
11
|
import { getMachineName } from "../machine-identity.js";
|
|
11
12
|
import { execFileSync } from "child_process";
|
|
@@ -1271,6 +1272,25 @@ export function runPostInitVerify(phrenPath) {
|
|
|
1271
1272
|
fix: ftsOk ? undefined : "Create a project: `cd ~/your-project && phren add`",
|
|
1272
1273
|
});
|
|
1273
1274
|
checks.push(getHookEntrypointCheck());
|
|
1275
|
+
// Check that the CLI wrapper at ~/.local/bin/phren exists and is executable (soft check — not mandatory)
|
|
1276
|
+
const cliWrapperPath = homePath(".local", "bin", "phren");
|
|
1277
|
+
let cliWrapperOk = false;
|
|
1278
|
+
try {
|
|
1279
|
+
const stat = fs.statSync(cliWrapperPath);
|
|
1280
|
+
cliWrapperOk = stat.isFile();
|
|
1281
|
+
if (cliWrapperOk)
|
|
1282
|
+
fs.accessSync(cliWrapperPath, fs.constants.X_OK);
|
|
1283
|
+
}
|
|
1284
|
+
catch {
|
|
1285
|
+
cliWrapperOk = false;
|
|
1286
|
+
}
|
|
1287
|
+
checks.push({
|
|
1288
|
+
name: "cli-wrapper",
|
|
1289
|
+
ok: true, // always pass — wrapper is optional (global install or npx work too)
|
|
1290
|
+
detail: cliWrapperOk
|
|
1291
|
+
? `CLI wrapper exists: ${cliWrapperPath}`
|
|
1292
|
+
: `CLI wrapper not found (optional — use 'npm i -g @phren/cli' or 'npx @phren/cli' instead)`,
|
|
1293
|
+
});
|
|
1274
1294
|
const ok = checks.every((c) => c.ok);
|
|
1275
1295
|
return { ok, checks };
|
|
1276
1296
|
}
|
package/mcp/dist/link/doctor.js
CHANGED
|
@@ -402,6 +402,15 @@ export async function runDoctor(phrenPath, fix = false, checkData = false) {
|
|
|
402
402
|
: `${tool} wrapper missing or not first in PATH`,
|
|
403
403
|
});
|
|
404
404
|
}
|
|
405
|
+
// Check phren CLI wrapper
|
|
406
|
+
const phrenCliActive = isWrapperActive("phren");
|
|
407
|
+
checks.push({
|
|
408
|
+
name: "wrapper:phren-cli",
|
|
409
|
+
ok: phrenCliActive,
|
|
410
|
+
detail: phrenCliActive
|
|
411
|
+
? "phren CLI wrapper active via ~/.local/bin/phren"
|
|
412
|
+
: "phren CLI wrapper missing — run init to install, or npm i -g @phren/cli",
|
|
413
|
+
});
|
|
405
414
|
if (fix) {
|
|
406
415
|
const repaired = repairPreexistingInstall(phrenPath);
|
|
407
416
|
const details = [];
|