@phren/cli 0.0.25 → 0.0.27
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/init.js +53 -8
- package/mcp/dist/link-doctor.js +29 -6
- package/package.json +1 -1
package/mcp/dist/init.js
CHANGED
|
@@ -85,19 +85,16 @@ function runSyncCommand(command, args) {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
function shouldUninstallCurrentGlobalPackage() {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return false;
|
|
88
|
+
// Always attempt to remove the global package if it exists, regardless of
|
|
89
|
+
// whether the uninstaller was invoked from the global install or a local repo.
|
|
91
90
|
const npmRootResult = runSyncCommand(getNpmCommand(), ["root", "-g"]);
|
|
92
91
|
if (!npmRootResult.ok)
|
|
93
92
|
return false;
|
|
94
93
|
const npmRoot = npmRootResult.stdout.trim();
|
|
95
94
|
if (!npmRoot)
|
|
96
95
|
return false;
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
return resolvedEntryScript === resolvedGlobalPackageRoot
|
|
100
|
-
|| resolvedEntryScript.startsWith(`${resolvedGlobalPackageRoot}${path.sep}`);
|
|
96
|
+
const globalPkgPath = path.join(npmRoot, PHREN_NPM_PACKAGE_NAME);
|
|
97
|
+
return fs.existsSync(globalPkgPath);
|
|
101
98
|
}
|
|
102
99
|
function uninstallCurrentGlobalPackage() {
|
|
103
100
|
const result = runSyncCommand(getNpmCommand(), ["uninstall", "-g", PHREN_NPM_PACKAGE_NAME]);
|
|
@@ -1682,8 +1679,29 @@ function sweepSkillSymlinks(phrenPath) {
|
|
|
1682
1679
|
log(` Removed skill symlink: ${fullPath}`);
|
|
1683
1680
|
}
|
|
1684
1681
|
}
|
|
1682
|
+
catch {
|
|
1683
|
+
// Broken symlink (target no longer exists) — clean it up
|
|
1684
|
+
try {
|
|
1685
|
+
fs.unlinkSync(fullPath);
|
|
1686
|
+
log(` Removed broken skill symlink: ${fullPath}`);
|
|
1687
|
+
}
|
|
1688
|
+
catch (err2) {
|
|
1689
|
+
debugLog(`sweepSkillSymlinks: could not remove broken symlink ${fullPath}: ${errorMessage(err2)}`);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
// Remove phren-generated manifest files from the skills parent directory
|
|
1694
|
+
const parentDir = path.dirname(dir);
|
|
1695
|
+
for (const manifestFile of ["skill-manifest.json", "skill-commands.json"]) {
|
|
1696
|
+
const manifestPath = path.join(parentDir, manifestFile);
|
|
1697
|
+
try {
|
|
1698
|
+
if (fs.existsSync(manifestPath)) {
|
|
1699
|
+
fs.unlinkSync(manifestPath);
|
|
1700
|
+
log(` Removed ${manifestFile} (${manifestPath})`);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1685
1703
|
catch (err) {
|
|
1686
|
-
debugLog(`sweepSkillSymlinks: could not
|
|
1704
|
+
debugLog(`sweepSkillSymlinks: could not remove ${manifestPath}: ${errorMessage(err)}`);
|
|
1687
1705
|
}
|
|
1688
1706
|
}
|
|
1689
1707
|
}
|
|
@@ -2008,6 +2026,33 @@ export async function runUninstall(opts = {}) {
|
|
|
2008
2026
|
if (shouldRemoveGlobalPackage) {
|
|
2009
2027
|
uninstallCurrentGlobalPackage();
|
|
2010
2028
|
}
|
|
2029
|
+
// Remove VS Code extension if installed
|
|
2030
|
+
try {
|
|
2031
|
+
const codeResult = execFileSync("code", ["--list-extensions"], {
|
|
2032
|
+
encoding: "utf8",
|
|
2033
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2034
|
+
timeout: 10_000,
|
|
2035
|
+
});
|
|
2036
|
+
const phrenExts = codeResult.split("\n").filter((ext) => ext.toLowerCase().includes("phren"));
|
|
2037
|
+
for (const ext of phrenExts) {
|
|
2038
|
+
const trimmed = ext.trim();
|
|
2039
|
+
if (!trimmed)
|
|
2040
|
+
continue;
|
|
2041
|
+
try {
|
|
2042
|
+
execFileSync("code", ["--uninstall-extension", trimmed], {
|
|
2043
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2044
|
+
timeout: 15_000,
|
|
2045
|
+
});
|
|
2046
|
+
log(` Removed VS Code extension (${trimmed})`);
|
|
2047
|
+
}
|
|
2048
|
+
catch (err) {
|
|
2049
|
+
debugLog(`uninstall: VS Code extension removal failed for ${trimmed}: ${errorMessage(err)}`);
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
catch {
|
|
2054
|
+
// code CLI not available — skip
|
|
2055
|
+
}
|
|
2011
2056
|
log(`\nPhren config, hooks, and installed data removed.`);
|
|
2012
2057
|
log(`Restart your agent(s) to apply changes.\n`);
|
|
2013
2058
|
}
|
package/mcp/dist/link-doctor.js
CHANGED
|
@@ -17,6 +17,7 @@ import { repairPreexistingInstall } from "./init-setup.js";
|
|
|
17
17
|
import { getMachineName, lookupProfile, findProfileFile, getProfileProjects, findProjectDir, } from "./link.js";
|
|
18
18
|
import { claudeProjectKey } from "./link-context.js";
|
|
19
19
|
import { getProjectOwnershipMode, readProjectConfig } from "./project-config.js";
|
|
20
|
+
import { readInstallPreferences } from "./init-preferences.js";
|
|
20
21
|
// ── Doctor ──────────────────────────────────────────────────────────────────
|
|
21
22
|
function isWrapperActive(tool) {
|
|
22
23
|
const wrapperPath = homePath(".local", "bin", tool);
|
|
@@ -35,7 +36,7 @@ function isWrapperActive(tool) {
|
|
|
35
36
|
return false;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
|
-
function gitRemoteStatus(phrenPath) {
|
|
39
|
+
function gitRemoteStatus(phrenPath, syncIntent) {
|
|
39
40
|
try {
|
|
40
41
|
execFileSync("git", ["-C", phrenPath, "rev-parse", "--is-inside-work-tree"], {
|
|
41
42
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -45,19 +46,40 @@ function gitRemoteStatus(phrenPath) {
|
|
|
45
46
|
catch {
|
|
46
47
|
return { ok: false, detail: "phren path is not a git repository" };
|
|
47
48
|
}
|
|
49
|
+
let remote;
|
|
48
50
|
try {
|
|
49
|
-
|
|
51
|
+
remote = execFileSync("git", ["-C", phrenPath, "remote", "get-url", "origin"], {
|
|
50
52
|
encoding: "utf8",
|
|
51
53
|
stdio: ["ignore", "pipe", "ignore"],
|
|
52
54
|
timeout: EXEC_TIMEOUT_QUICK_MS,
|
|
53
55
|
}).trim();
|
|
54
|
-
return remote
|
|
55
|
-
? { ok: true, detail: `origin=${remote}` }
|
|
56
|
-
: { ok: true, detail: "no remote configured (local-only sync mode)" };
|
|
57
56
|
}
|
|
58
57
|
catch {
|
|
58
|
+
// no remote configured
|
|
59
|
+
}
|
|
60
|
+
if (!remote) {
|
|
61
|
+
if (syncIntent === "sync") {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
detail: "sync configured but no git remote found. Run: cd ~/.phren && git remote add origin <YOUR_REPO_URL> && git push -u origin main",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
59
67
|
return { ok: true, detail: "no remote configured (local-only sync mode)" };
|
|
60
68
|
}
|
|
69
|
+
// Remote exists — verify it's reachable
|
|
70
|
+
try {
|
|
71
|
+
execFileSync("git", ["-C", phrenPath, "ls-remote", "--exit-code", "origin"], {
|
|
72
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
73
|
+
timeout: 10_000,
|
|
74
|
+
});
|
|
75
|
+
return { ok: true, detail: `origin=${remote}` };
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
if (syncIntent === "sync") {
|
|
79
|
+
return { ok: false, detail: `origin=${remote} (unreachable) — check your network or SSH keys` };
|
|
80
|
+
}
|
|
81
|
+
return { ok: true, detail: `origin=${remote} (unreachable, local-only mode)` };
|
|
82
|
+
}
|
|
61
83
|
}
|
|
62
84
|
function pushSkillMirrorChecks(checks, scope, manifest, destDir) {
|
|
63
85
|
const parentDir = path.dirname(destDir);
|
|
@@ -117,7 +139,8 @@ export async function runDoctor(phrenPath, fix = false, checkData = false) {
|
|
|
117
139
|
ok: versionAtLeast(nodeVersion, 20),
|
|
118
140
|
detail: nodeVersion || "node not found in PATH",
|
|
119
141
|
});
|
|
120
|
-
const
|
|
142
|
+
const prefs = readInstallPreferences(phrenPath);
|
|
143
|
+
const gitRemote = gitRemoteStatus(phrenPath, prefs.syncIntent);
|
|
121
144
|
checks.push({
|
|
122
145
|
name: "git-remote",
|
|
123
146
|
ok: gitRemote.ok,
|