From 8f1ea708fe4430f3cf3953eb3c60efcef8057338 Mon Sep 17 00:00:00 2001 From: execsumo Date: Sat, 13 Jun 2026 13:28:14 -0700 Subject: [PATCH] feat: change default macOS data path to ~/.skill-manager with legacy fallback --- README.md | 18 +++++++++--------- README.zh-CN.md | 18 +++++++++--------- .../skills/screens/ScanConfigPage.test.tsx | 2 +- skill_manager/paths.py | 3 ++- tests/unit/test_paths.py | 14 ++++++++++++-- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ddeb90f..3abfb6b 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ Actions that can change local state include: - creating, updating, syncing, importing, or deleting a slash command - changing harness support settings -App-owned files live under `~/Library/Application Support/skill-manager` on macOS and XDG base directories on Linux. +App-owned files live under `~/.skill-manager` on macOS (with a legacy fallback to `~/Library/Application Support/skill-manager` if it already exists) and XDG base directories on Linux. ## How it works @@ -242,17 +242,17 @@ CLI marketplace entries are preview-only. ## Configuration -On macOS, app-owned files live under `~/Library/Application Support/skill-manager`. On Linux, app-owned files use XDG base directories. +On macOS, app-owned files live under `~/.skill-manager` (with a legacy fallback to `~/Library/Application Support/skill-manager` if it already exists). On Linux, app-owned files use XDG base directories. Useful macOS paths: -- shared skills store: `~/Library/Application Support/skill-manager/shared` -- MCP manifest: `~/Library/Application Support/skill-manager/mcp/manifest.json` -- slash command library: `~/Library/Application Support/skill-manager/slash-commands/commands` -- slash command sync state: `~/Library/Application Support/skill-manager/slash-commands/sync-state.json` -- marketplace cache: `~/Library/Application Support/skill-manager/marketplace` -- app database and LLM scan configs: `~/Library/Application Support/skill-manager/skill-manager.db` -- app settings: `~/Library/Application Support/skill-manager/settings.json` +- shared skills store: `~/.skill-manager/shared` +- MCP manifest: `~/.skill-manager/mcp/manifest.json` +- slash command library: `~/.skill-manager/slash-commands/commands` +- slash command sync state: `~/.skill-manager/slash-commands/sync-state.json` +- marketplace cache: `~/.skill-manager/marketplace` +- app database and LLM scan configs: `~/.skill-manager/skill-manager.db` +- app settings: `~/.skill-manager/settings.json` Useful Linux paths: diff --git a/README.zh-CN.md b/README.zh-CN.md index c69a46b..9b3da48 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -163,7 +163,7 @@ Skill Manager 是本地配置管理工具。它在你的机器上运行,并读 - 创建、更新、同步、导入或删除 slash command - 修改 harness 支持设置 -在 macOS 上,应用拥有的文件位于 `~/Library/Application Support/skill-manager`;在 Linux 上使用 XDG base directories。 +在 macOS 上,应用拥有的文件位于 `~/.skill-manager`(如果已存在,则回退到 `~/Library/Application Support/skill-manager`);在 Linux 上使用 XDG base directories。 ## 工作方式 @@ -212,17 +212,17 @@ CLI marketplace 条目仅用于预览。 ## 配置 -在 macOS 上,应用拥有的文件位于 `~/Library/Application Support/skill-manager`;在 Linux 上使用 XDG base directories。 +在 macOS 上,应用拥有的文件位于 `~/.skill-manager`(如果已存在,则回退到 `~/Library/Application Support/skill-manager`);在 Linux 上使用 XDG base directories。 常用 macOS 路径: -- 共享 Skill 存储:`~/Library/Application Support/skill-manager/shared` -- MCP manifest:`~/Library/Application Support/skill-manager/mcp/manifest.json` -- slash command 库:`~/Library/Application Support/skill-manager/slash-commands/commands` -- slash command 同步状态:`~/Library/Application Support/skill-manager/slash-commands/sync-state.json` -- 商城缓存:`~/Library/Application Support/skill-manager/marketplace` -- 应用数据库和 LLM 扫描配置:`~/Library/Application Support/skill-manager/skill-manager.db` -- 应用设置:`~/Library/Application Support/skill-manager/settings.json` +- 共享 Skill 存储:`~/.skill-manager/shared` +- MCP manifest:`~/.skill-manager/mcp/manifest.json` +- slash command 库:`~/.skill-manager/slash-commands/commands` +- slash command 同步状态:`~/.skill-manager/slash-commands/sync-state.json` +- 商城缓存:`~/.skill-manager/marketplace` +- 应用数据库和 LLM 扫描配置:`~/.skill-manager/skill-manager.db` +- 应用设置:`~/.skill-manager/settings.json` 常用 Linux 路径: diff --git a/frontend/src/features/skills/screens/ScanConfigPage.test.tsx b/frontend/src/features/skills/screens/ScanConfigPage.test.tsx index 002b1db..fd36baa 100644 --- a/frontend/src/features/skills/screens/ScanConfigPage.test.tsx +++ b/frontend/src/features/skills/screens/ScanConfigPage.test.tsx @@ -110,7 +110,7 @@ describe("ScanConfigPage", () => { expect(screen.queryByText(/Missing required fields: API Key/)).not.toBeInTheDocument(); expect(screen.getByRole("button", { name: "Update" })).toBeDisabled(); expect(screen.queryByRole("columnheader", { name: "Last validation" })).not.toBeInTheDocument(); - expect(screen.getByLabelText("Last validation")).toHaveTextContent(/May 12|12 May|Failed|Not validated/); + expect(screen.getByLabelText("Last validation")).toHaveTextContent(/May 11|11 May|May 12|12 May|Failed|Not validated/); const apiKeyInput = screen.getByLabelText("API Key", { selector: "input" }); expect(apiKeyInput).toHaveAttribute("type", "password"); expect(String(apiKeyInput.getAttribute("value") ?? "")).not.toBe(""); diff --git a/skill_manager/paths.py b/skill_manager/paths.py index 9154b70..735ac17 100644 --- a/skill_manager/paths.py +++ b/skill_manager/paths.py @@ -59,7 +59,8 @@ def _base_dirs(context: PlatformContext) -> tuple[Path, Path, Path]: state_override = context.env.get(STATE_DIR_ENV) if context.platform == "macos": - default_macos = context.home / "Library" / "Application Support" / APP_NAME + legacy_dir = context.home / "Library" / "Application Support" / APP_NAME + default_macos = legacy_dir if legacy_dir.is_dir() else context.home / f".{APP_NAME}" config_dir = _xdg_dir(context.env, "XDG_CONFIG_HOME", default_macos) data_dir = _xdg_dir(context.env, "XDG_DATA_HOME", default_macos) state_dir = ( diff --git a/tests/unit/test_paths.py b/tests/unit/test_paths.py index 0dc8845..e3e2af9 100644 --- a/tests/unit/test_paths.py +++ b/tests/unit/test_paths.py @@ -26,11 +26,11 @@ def isolated_env(platform: str): class ResolveAppPathsTests(unittest.TestCase): - def test_macos_default_layout_collapses_to_application_support(self) -> None: + def test_macos_default_layout_collapses_to_dot_dir(self) -> None: with isolated_env("darwin"), TemporaryDirectory() as temp: home = Path(temp) / "home" paths = resolve_app_paths({"HOME": str(home)}) - base = home / "Library" / "Application Support" / APP_NAME + base = home / f".{APP_NAME}" self.assertEqual(paths.config_dir, base) self.assertEqual(paths.data_dir, base) self.assertEqual(paths.state_dir, base) @@ -44,6 +44,16 @@ def test_macos_default_layout_collapses_to_application_support(self) -> None: self.assertEqual(paths.runtime_state_path, base / "runtime.json") self.assertEqual(paths.server_log_path, base / "server.log") + def test_macos_default_layout_falls_back_to_legacy_application_support_if_exists(self) -> None: + with isolated_env("darwin"), TemporaryDirectory() as temp: + home = Path(temp) / "home" + legacy_dir = home / "Library" / "Application Support" / APP_NAME + legacy_dir.mkdir(parents=True) + paths = resolve_app_paths({"HOME": str(home)}) + self.assertEqual(paths.config_dir, legacy_dir) + self.assertEqual(paths.data_dir, legacy_dir) + self.assertEqual(paths.state_dir, legacy_dir) + def test_xdg_overrides_each_dir_independently(self) -> None: with isolated_env("darwin"), TemporaryDirectory() as temp: root = Path(temp)