Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 40 additions & 16 deletions src/cli/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -900,13 +900,26 @@ cbm_detected_agents_t cbm_detect_agents(const char *home_dir) {
}

/* Zed: platform-specific config dir */
{
#ifdef __APPLE__
snprintf(path, sizeof(path), "%s/Library/Application Support/Zed", home_dir);
const char *zed_base = cbm_app_config_dir();
if (zed_base) {
snprintf(path, sizeof(path), "%s/Library/Application Support/Zed", zed_base);
}
#elif defined(_WIN32)
const char *zed_base = cbm_app_local_dir();
if (zed_base) {
snprintf(path, sizeof(path), "%s/Zed", zed_base);
}
#else
snprintf(path, sizeof(path), "%s/.config/zed", home_dir);
const char *zed_base = cbm_app_config_dir();
if (zed_base) {
snprintf(path, sizeof(path), "%s/zed", zed_base);
}
#endif
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
agents.zed = true;
if (zed_base && stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
agents.zed = true;
}
}

/* OpenCode: binary on PATH */
Expand Down Expand Up @@ -935,13 +948,18 @@ cbm_detected_agents_t cbm_detect_agents(const char *home_dir) {
}

/* VS Code: User config dir */
{
const char *app_cfg = cbm_app_config_dir();
if (app_cfg) {
#ifdef __APPLE__
snprintf(path, sizeof(path), "%s/Library/Application Support/Code/User", home_dir);
snprintf(path, sizeof(path), "%s/Library/Application Support/Code/User", app_cfg);
#else
snprintf(path, sizeof(path), "%s/.config/Code/User", home_dir);
snprintf(path, sizeof(path), "%s/Code/User", app_cfg);
#endif
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
agents.vscode = true;
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
agents.vscode = true;
}
}
}

/* OpenClaw: ~/.openclaw/ dir */
Expand Down Expand Up @@ -2510,9 +2528,11 @@ static void cbm_install_agent_configs(const char *home, const char *binary_path,
char config_path[1024];
#ifdef __APPLE__
snprintf(config_path, sizeof(config_path),
"%s/Library/Application Support/Zed/settings.json", home);
"%s/Library/Application Support/Zed/settings.json", cbm_app_config_dir());
#elif defined(_WIN32)
snprintf(config_path, sizeof(config_path), "%s/Zed/settings.json", cbm_app_local_dir());
#else
snprintf(config_path, sizeof(config_path), "%s/.config/zed/settings.json", home);
snprintf(config_path, sizeof(config_path), "%s/zed/settings.json", cbm_app_config_dir());
#endif
if (!dry_run) {
cbm_install_zed_mcp(binary_path, config_path);
Expand Down Expand Up @@ -2591,11 +2611,12 @@ static void cbm_install_agent_configs(const char *home, const char *binary_path,
if (agents.vscode) {
printf("VS Code:\n");
char config_path[1024];
const char *vs_cfg = cbm_app_config_dir();
#ifdef __APPLE__
snprintf(config_path, sizeof(config_path),
"%s/Library/Application Support/Code/User/mcp.json", home);
"%s/Library/Application Support/Code/User/mcp.json", vs_cfg);
#else
snprintf(config_path, sizeof(config_path), "%s/.config/Code/User/mcp.json", home);
snprintf(config_path, sizeof(config_path), "%s/Code/User/mcp.json", vs_cfg);
#endif
if (!dry_run) {
cbm_install_vscode_mcp(binary_path, config_path);
Expand Down Expand Up @@ -2812,9 +2833,11 @@ int cbm_cmd_uninstall(int argc, char **argv) {
char config_path[1024];
#ifdef __APPLE__
snprintf(config_path, sizeof(config_path),
"%s/Library/Application Support/Zed/settings.json", home);
"%s/Library/Application Support/Zed/settings.json", cbm_app_config_dir());
#elif defined(_WIN32)
snprintf(config_path, sizeof(config_path), "%s/Zed/settings.json", cbm_app_local_dir());
#else
snprintf(config_path, sizeof(config_path), "%s/.config/zed/settings.json", home);
snprintf(config_path, sizeof(config_path), "%s/zed/settings.json", cbm_app_config_dir());
#endif
if (!dry_run) {
cbm_remove_zed_mcp(config_path);
Expand Down Expand Up @@ -2883,11 +2906,12 @@ int cbm_cmd_uninstall(int argc, char **argv) {

if (agents.vscode) {
char config_path[1024];
const char *vs_cfg = cbm_app_config_dir();
#ifdef __APPLE__
snprintf(config_path, sizeof(config_path),
"%s/Library/Application Support/Code/User/mcp.json", home);
"%s/Library/Application Support/Code/User/mcp.json", vs_cfg);
#else
snprintf(config_path, sizeof(config_path), "%s/.config/Code/User/mcp.json", home);
snprintf(config_path, sizeof(config_path), "%s/Code/User/mcp.json", vs_cfg);
#endif
if (!dry_run) {
cbm_remove_vscode_mcp(config_path);
Expand Down
64 changes: 64 additions & 0 deletions src/foundation/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,40 @@ char *cbm_normalize_path_sep(char *path) {
return path;
}

const char *cbm_app_config_dir(void) {
static char buf[1024];
// NOLINTNEXTLINE(concurrency-mt-unsafe)
const char *d = getenv("APPDATA");
if (!d || !d[0]) {
const char *home = cbm_get_home_dir();
if (!home) {
return NULL;
}
snprintf(buf, sizeof(buf), "%s/AppData/Roaming", home);
return buf;
}
snprintf(buf, sizeof(buf), "%s", d);
cbm_normalize_path_sep(buf);
return buf;
}

const char *cbm_app_local_dir(void) {
static char buf[1024];
// NOLINTNEXTLINE(concurrency-mt-unsafe)
const char *d = getenv("LOCALAPPDATA");
if (!d || !d[0]) {
const char *home = cbm_get_home_dir();
if (!home) {
return NULL;
}
snprintf(buf, sizeof(buf), "%s/AppData/Local", home);
return buf;
}
snprintf(buf, sizeof(buf), "%s", d);
cbm_normalize_path_sep(buf);
return buf;
}

#else /* POSIX (macOS + Linux) */

/* ── POSIX implementation ─────────────────────────────────────── */
Expand Down Expand Up @@ -261,3 +295,33 @@ const char *cbm_get_home_dir(void) {
}
return NULL;
}

#ifndef _WIN32
const char *cbm_app_config_dir(void) {
static char buf[1024];
#ifdef __APPLE__
/* macOS: callers prepend "Library/Application Support/..." */
return cbm_get_home_dir();
#else
/* Linux: XDG_CONFIG_HOME or ~/.config */
// NOLINTNEXTLINE(concurrency-mt-unsafe)
const char *xdg = getenv("XDG_CONFIG_HOME");
if (xdg && xdg[0]) {
snprintf(buf, sizeof(buf), "%s", xdg);
return buf;
}
const char *home = cbm_get_home_dir();
if (!home) {
return NULL;
}
snprintf(buf, sizeof(buf), "%s/.config", home);
return buf;
#endif
}

const char *cbm_app_local_dir(void) {
/* On POSIX there is no distinction between "roaming" and "local" app data.
* Delegate to cbm_app_config_dir() so callers compile on all platforms. */
return cbm_app_config_dir();
}
#endif /* _WIN32 */
11 changes: 11 additions & 0 deletions src/foundation/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ int cbm_default_worker_count(bool initial);
* Returns NULL when neither is set. */
const char *cbm_get_home_dir(void);

/* App data directories (cross-platform).
* Windows: APPDATA (Roaming) — e.g. C:/Users/.../AppData/Roaming
* macOS: HOME (callers append "Library/Application Support/...")
* Linux: XDG_CONFIG_HOME or ~/.config
* Returns NULL when the directory cannot be determined. */
const char *cbm_app_config_dir(void);

/* Windows: LOCALAPPDATA — e.g. C:/Users/.../AppData/Local
* macOS/Linux: same as cbm_app_config_dir(). */
const char *cbm_app_local_dir(void);

/* ── File system ───────────────────────────────────────────────── */

/* Check if a path exists. */
Expand Down