diff --git a/src/cli/cli.c b/src/cli/cli.c index d981af266..e90d8b885 100644 --- a/src/cli/cli.c +++ b/src/cli/cli.c @@ -9699,6 +9699,27 @@ static const char *cli_external_manager_name(const char *self_path) { if (strstr(self_path, "/.cargo/bin/")) { return "cargo"; } +#ifdef __FreeBSD__ + /* FreeBSD ports/pkg install under ${LOCALBASE} (default /usr/local). pkg owns + * that file, so install must not drop a second copy in ~/.local/bin or edit a + * shell rc, and update must defer to pkg(8). Anchor at the start so a manual + * `install --dir=/opt/...` elsewhere is still treated as ours; --force-binary + * is the escape hatch for anyone who really does self-manage that prefix. + * + * LOCALBASE is configurable, so the port passes its real PREFIX via + * -DCBM_PKG_PREFIX; when it is absent we fall back to the documented default. */ +#ifdef CBM_PKG_PREFIX + if (strncmp(self_path, CBM_PKG_PREFIX "/bin/", sizeof(CBM_PKG_PREFIX "/bin/") - 1) == 0 || + strncmp(self_path, CBM_PKG_PREFIX "/sbin/", sizeof(CBM_PKG_PREFIX "/sbin/") - 1) == 0) { + return "FreeBSD pkg"; + } +#else + if (strncmp(self_path, "/usr/local/bin/", 15) == 0 || + strncmp(self_path, "/usr/local/sbin/", 16) == 0) { + return "FreeBSD pkg"; + } +#endif +#endif return NULL; } @@ -12227,6 +12248,8 @@ int cbm_cmd_update(int argc, char **argv) { (void)fprintf(stderr, " update it with: mise upgrade codebase-memory-mcp\n"); } else if (manager && strcmp(manager, "Homebrew") == 0) { (void)fprintf(stderr, " update it with: brew upgrade codebase-memory-mcp\n"); + } else if (manager && strcmp(manager, "FreeBSD pkg") == 0) { + (void)fprintf(stderr, " update it with: pkg upgrade codebase-memory-mcp\n"); } else { (void)fprintf(stderr, " update it through whichever tool installed it.\n"); } diff --git a/src/ui/http_server.c b/src/ui/http_server.c index 18589d8d8..409267358 100644 --- a/src/ui/http_server.c +++ b/src/ui/http_server.c @@ -63,6 +63,10 @@ #ifdef __APPLE__ #include #endif +#ifdef __FreeBSD__ +#include +#include +#endif /* ── Constants ────────────────────────────────────────────────── */ @@ -1003,6 +1007,15 @@ static bool resolve_self_executable(char *out, size_t outsz) { return copy_path(out, outsz, buf); } return false; +#elif defined(__FreeBSD__) + /* No /proc by default on FreeBSD; ask the kernel for our own path. */ + char buf[1024]; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + size_t cb = sizeof(buf); + if (sysctl(mib, 4, buf, &cb, NULL, 0) == 0 && cb > 0) { + return copy_path(out, outsz, buf); + } + return false; #else char buf[1024]; ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); diff --git a/tests/test_cli.c b/tests/test_cli.c index 95fff9d76..f9f2ed99d 100644 --- a/tests/test_cli.c +++ b/tests/test_cli.c @@ -12752,8 +12752,16 @@ TEST(cli_external_manager_detection_needs_positive_evidence_issue1566) { ASSERT_NULL( cbm_cli_external_manager_name_for_testing("/Users/x/.local/bin/codebase-memory-mcp")); ASSERT_NULL(cbm_cli_external_manager_name_for_testing("/opt/cbm/codebase-memory-mcp")); - ASSERT_NULL(cbm_cli_external_manager_name_for_testing("/usr/local/bin/codebase-memory-mcp")); ASSERT_NULL(cbm_cli_external_manager_name_for_testing("build/c/test-runner")); +#ifdef __FreeBSD__ + /* On FreeBSD the port/pkg owns ${LOCALBASE}/bin, so a binary there IS + * externally managed; elsewhere the same path is unremarkable. */ + ASSERT_NOT_NULL( + cbm_cli_external_manager_name_for_testing("/usr/local/bin/codebase-memory-mcp")); + ASSERT_NULL(cbm_cli_external_manager_name_for_testing("/opt/local/bin/codebase-memory-mcp")); +#else + ASSERT_NULL(cbm_cli_external_manager_name_for_testing("/usr/local/bin/codebase-memory-mcp")); +#endif ASSERT_NULL(cbm_cli_external_manager_name_for_testing("")); ASSERT_NULL(cbm_cli_external_manager_name_for_testing(NULL)); PASS();