diff --git a/README.md b/README.md
index ddeb90f..82819b9 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,7 @@ AI extensions are scattered across harness-specific folders, MCP config files, s
| **Needs review** | Skill Manager found local state, config differences, or inventory issues that need a decision. |
| **Scan** | Run LLM-backed security checks against Skills before trusting them. |
| **Discover** | Browse marketplaces and preview external tools. |
+| **Settings** | Enable or disable harness support and inspect the local paths Skill Manager will use. |
## What you can do
@@ -41,6 +42,7 @@ AI extensions are scattered across harness-specific folders, MCP config files, s
- Install or adopt MCP server configs, resolve differences, and enable them where supported.
- Manage reusable slash commands once, then sync them to supported harnesses.
- Discover Skills, MCP servers, and preview-only CLI tools from marketplace sources.
+- Toggle harness support when a local agent is missing, unsupported, or intentionally out of scope.
## Product tour
@@ -74,11 +76,11 @@ Typical flow:
3. Run a scan for one Skill, selected Skills, or the full visible list.
4. Review severity, findings, snippets, and remediation guidance.
-
+
Scan configurations are managed separately so you can save multiple providers, choose one active configuration, and keep API keys masked in list views.
-
+
### MCP servers
@@ -135,6 +137,19 @@ skill-manager start
The npm wrapper downloads the native release artifact for the current platform and CPU architecture.
+### CLI commands
+
+`skill-manager start` launches one managed background instance and opens the browser. The CLI also supports:
+
+```bash
+skill-manager serve # run the app server in the foreground
+skill-manager start # launch one managed background instance
+skill-manager status # print the managed instance URL and pid
+skill-manager stop # stop the managed background instance
+```
+
+Useful launch flags include `--host`, `--port`, `--frontend-dist`, `--state-dir`, and `--no-open-browser`.
+
## Supported harnesses
@@ -195,8 +210,23 @@ Actions that can change local state include:
App-owned files live under `~/Library/Application Support/skill-manager` on macOS and XDG base directories on Linux.
+Marketplace requests go to external registries or catalog sources, and Skill scans send selected Skill context to the configured LLM provider. Local mutation operations are explicit user actions from the UI or API.
+
## How it works
+### App architecture
+
+Skill Manager is a local FastAPI app with a React/TypeScript frontend. The Python backend owns filesystem and config mutations, exposes JSON APIs under `/api`, and serves the built frontend for the desktop browser experience. The frontend uses React Router for pages, TanStack Query for cached API state, and shared design-system primitives for cards, matrices, dialogs, toasts, filters, and harness icons.
+
+The backend is organized around feature services:
+
+- `skills` reads harness skill folders, manages the shared local store, and installs marketplace Skills.
+- `mcp` normalizes server configs, translates them into harness-specific config files, and checks local availability.
+- `slash_commands` stores shared command definitions and syncs them into each harness format.
+- `scan` stores LLM scan configs in SQLite and runs bounded Skill security analysis.
+- `settings` reports local paths and persists harness support toggles.
+- `marketplace` caches Skills, MCP registry, and CLIs.dev catalog responses.
+
### Skills
Before adoption, each harness points at its own local skill folder. After adoption, Skill Manager keeps one canonical package in its shared local store and exposes it to selected harnesses with local links. Disabling a harness removes that harness binding without deleting the package.
@@ -253,6 +283,8 @@ Useful macOS paths:
- 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`
+- managed runtime state: `~/Library/Application Support/skill-manager/runtime.json`
+- managed server log: `~/Library/Application Support/skill-manager/server.log`
Useful Linux paths:
@@ -263,6 +295,8 @@ Useful Linux paths:
- marketplace cache: `${XDG_DATA_HOME:-~/.local/share}/skill-manager/marketplace`
- app database and LLM scan configs: `${XDG_DATA_HOME:-~/.local/share}/skill-manager/skill-manager.db`
- app settings: `${XDG_CONFIG_HOME:-~/.config}/skill-manager/settings.json`
+- managed runtime state: `${XDG_STATE_HOME:-~/.local/state}/skill-manager/runtime.json`
+- managed server log: `${XDG_STATE_HOME:-~/.local/state}/skill-manager/server.log`
Most users do not need to change these locations. If you manage skills in a custom environment, you can override individual skill roots with environment variables.
@@ -276,6 +310,12 @@ Most users do not need to change these locations. If you manage skills in a cust
MCP config locations are harness-owned. Skill Manager writes only to verified config paths and skips unsupported harness writes.
+Other useful overrides:
+
+- `SKILL_MANAGER_SETTINGS_PATH`: use a specific settings JSON file.
+- `SKILL_MANAGER_STATE_DIR`: use a specific runtime state/log directory for `start`, `stop`, and `status`.
+- `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, `XDG_STATE_HOME`: change the base directories used on Linux, and also on macOS when explicitly set.
+
## From source
### Requirements
@@ -327,11 +367,14 @@ npm test
npm run build
```
+Backend tests are split between `tests/unit` and `tests/integration`. Frontend tests live beside the component, model, or screen they cover, with shared test helpers under `frontend/src/test`.
+
## Troubleshooting
- If Marketplace requests fail with `Marketplace is temporarily unavailable`, verify your network connection and try again.
- On macOS, if `npm install -g @mode-io/skill-manager` reports that Homebrew already owns `skill-manager`, uninstall the Homebrew formula first. The inverse also applies: uninstall the npm package before switching back to Homebrew.
- If an MCP harness is shown as unavailable, Skill Manager has detected that the local client is missing or does not support the required config surface.
+- If `skill-manager start` fails, run `skill-manager status`, then inspect the managed server log listed in the configuration section or pass `--state-dir` to isolate a test launch.
## More to come
diff --git a/README.zh-CN.md b/README.zh-CN.md
index c69a46b..7e08d79 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -32,6 +32,7 @@ AI 扩展通常分散在各个 harness 自己的文件夹、MCP 配置文件、s
| **待确认** | Skill Manager 发现了本地状态、配置差异或库存问题,需要你先做决定。 |
| **扫描** | 在信任某个 Skill 之前,使用 LLM 驱动的安全检查进行确认。 |
| **发现** | 浏览商城,并预览外部工具。 |
+| **设置** | 启用或停用 harness 支持,并查看 Skill Manager 将使用的本地路径。 |
## 你可以做什么
@@ -41,6 +42,7 @@ AI 扩展通常分散在各个 harness 自己的文件夹、MCP 配置文件、s
- 安装或采用 MCP 服务器配置,解决配置差异,并写入支持的 harness。
- 统一管理可复用的 slash command,并同步到支持的 harness。
- 从商城来源发现 Skill、MCP 服务器,以及仅预览的 CLI 工具。
+- 当本地 agent 缺失、不支持或暂不纳入管理时,可以切换对应 harness 的支持状态。
## 产品导览
@@ -74,11 +76,11 @@ AI 扩展通常分散在各个 harness 自己的文件夹、MCP 配置文件、s
3. 对单个 Skill、已选 Skill 或当前可见列表运行扫描。
4. 查看严重程度、发现项、代码片段和修复建议。
-
+
扫描配置单独管理,因此你可以保存多个 provider,选择一个当前配置,并且在列表中只显示隐藏后的 API Key。
-
+
### MCP 服务器
@@ -135,6 +137,19 @@ skill-manager start
npm wrapper 会为当前平台和 CPU 架构下载对应的原生 release artifact。
+### CLI 命令
+
+`skill-manager start` 会启动一个托管的后台实例并打开浏览器。CLI 还支持:
+
+```bash
+skill-manager serve # 在前台运行 app server
+skill-manager start # 启动一个托管后台实例
+skill-manager status # 打印托管实例 URL 和 pid
+skill-manager stop # 停止托管后台实例
+```
+
+常用启动参数包括 `--host`、`--port`、`--frontend-dist`、`--state-dir` 和 `--no-open-browser`。
+
## 支持的 harness
| Harness | Skill | MCP 服务器 | Slash command |
@@ -165,8 +180,23 @@ Skill Manager 是本地配置管理工具。它在你的机器上运行,并读
在 macOS 上,应用拥有的文件位于 `~/Library/Application Support/skill-manager`;在 Linux 上使用 XDG base directories。
+商城请求会访问外部 registry 或 catalog 来源;Skill 扫描会将所选 Skill 上下文发送给已配置的 LLM provider。所有本地状态变更都来自 UI 或 API 中的显式用户操作。
+
## 工作方式
+### 应用架构
+
+Skill Manager 是一个本地 FastAPI 应用,配套 React/TypeScript 前端。Python 后端负责文件系统和配置变更,在 `/api` 下提供 JSON API,并为浏览器体验提供构建后的前端静态资源。前端使用 React Router 管理页面,使用 TanStack Query 缓存 API 状态,并复用卡片、矩阵、对话框、toast、筛选器和 harness 图标等设计系统组件。
+
+后端按功能服务组织:
+
+- `skills` 读取 harness Skill 文件夹,管理共享本地存储,并安装商城 Skill。
+- `mcp` 规范化服务器配置,将其转换为各 harness 专用配置文件,并检查本地可用性。
+- `slash_commands` 保存共享 command 定义,并同步到每个 harness 的格式。
+- `scan` 在 SQLite 中保存 LLM 扫描配置,并运行受限的 Skill 安全分析。
+- `settings` 报告本地路径并持久化 harness 支持开关。
+- `marketplace` 缓存 Skills、MCP registry 和 CLIs.dev catalog 响应。
+
### Skill
采用之前,各 harness 指向各自的本地 Skill 文件夹。采用之后,Skill Manager 会在共享本地存储中保留一个规范包,并通过本地链接暴露给选定 harness。停用某个 harness 会移除该 harness 绑定,但不会删除包本身。
@@ -223,6 +253,8 @@ CLI marketplace 条目仅用于预览。
- 商城缓存:`~/Library/Application Support/skill-manager/marketplace`
- 应用数据库和 LLM 扫描配置:`~/Library/Application Support/skill-manager/skill-manager.db`
- 应用设置:`~/Library/Application Support/skill-manager/settings.json`
+- 托管运行时状态:`~/Library/Application Support/skill-manager/runtime.json`
+- 托管 server 日志:`~/Library/Application Support/skill-manager/server.log`
常用 Linux 路径:
@@ -233,6 +265,8 @@ CLI marketplace 条目仅用于预览。
- 商城缓存:`${XDG_DATA_HOME:-~/.local/share}/skill-manager/marketplace`
- 应用数据库和 LLM 扫描配置:`${XDG_DATA_HOME:-~/.local/share}/skill-manager/skill-manager.db`
- 应用设置:`${XDG_CONFIG_HOME:-~/.config}/skill-manager/settings.json`
+- 托管运行时状态:`${XDG_STATE_HOME:-~/.local/state}/skill-manager/runtime.json`
+- 托管 server 日志:`${XDG_STATE_HOME:-~/.local/state}/skill-manager/server.log`
大多数用户不需要修改这些位置。如果你在自定义环境中管理 Skill,可以用环境变量覆盖单个 Skill 根目录。
@@ -246,6 +280,12 @@ CLI marketplace 条目仅用于预览。
MCP 配置位置由 harness 拥有。Skill Manager 只写入经过验证的配置路径,并跳过不支持的 harness 写入。
+其他常用覆盖项:
+
+- `SKILL_MANAGER_SETTINGS_PATH`:使用指定的 settings JSON 文件。
+- `SKILL_MANAGER_STATE_DIR`:为 `start`、`stop` 和 `status` 使用指定运行时状态/日志目录。
+- `XDG_CONFIG_HOME`、`XDG_DATA_HOME`、`XDG_STATE_HOME`:改变 Linux 上使用的基础目录;在 macOS 上显式设置时也会生效。
+
## 从源码运行
### 要求
@@ -297,11 +337,14 @@ npm test
npm run build
```
+后端测试分为 `tests/unit` 和 `tests/integration`。前端测试与其保护的组件、模型或页面放在一起,共享测试 helper 位于 `frontend/src/test`。
+
## 故障排查
- 如果商城请求失败并显示 `Marketplace is temporarily unavailable`,请确认网络连接后重试。
- 在 macOS 上,如果 `npm install -g @mode-io/skill-manager` 提示 Homebrew 已拥有 `skill-manager`,请先卸载 Homebrew formula。反过来也一样:切回 Homebrew 前请先卸载 npm 包。
- 如果某个 MCP harness 显示为不可用,说明 Skill Manager 检测到本地客户端缺失,或该客户端不支持所需配置界面。
+- 如果 `skill-manager start` 启动失败,可以先运行 `skill-manager status`,再查看配置章节列出的托管 server 日志;也可以传入 `--state-dir` 隔离一次测试启动。
## 后续计划
diff --git a/assets/skill-manager-scan-config.png b/assets/skill-manager-scan-config.png
new file mode 100644
index 0000000..5c2c989
Binary files /dev/null and b/assets/skill-manager-scan-config.png differ
diff --git a/assets/skill-manager-scan-config.svg b/assets/skill-manager-scan-config.svg
deleted file mode 100644
index efff56b..0000000
--- a/assets/skill-manager-scan-config.svg
+++ /dev/null
@@ -1,81 +0,0 @@
-
diff --git a/assets/skill-manager-scan-view.png b/assets/skill-manager-scan-view.png
new file mode 100644
index 0000000..19c50fd
Binary files /dev/null and b/assets/skill-manager-scan-view.png differ
diff --git a/assets/skill-manager-scan-view.svg b/assets/skill-manager-scan-view.svg
deleted file mode 100644
index 9578603..0000000
--- a/assets/skill-manager-scan-view.svg
+++ /dev/null
@@ -1,71 +0,0 @@
-
diff --git a/frontend/src/features/mcp/components/McpHarnessLogoStack.test.tsx b/frontend/src/features/mcp/components/McpHarnessLogoStack.test.tsx
new file mode 100644
index 0000000..96fe293
--- /dev/null
+++ b/frontend/src/features/mcp/components/McpHarnessLogoStack.test.tsx
@@ -0,0 +1,57 @@
+import { render } from "@testing-library/react";
+import { describe, expect, it } from "vitest";
+
+import type { McpBindingDto, McpInventoryColumnDto } from "../api/management-types";
+import { McpHarnessLogoStack } from "./McpHarnessLogoStack";
+
+describe("McpHarnessLogoStack", () => {
+ const columns: McpInventoryColumnDto[] = [
+ { harness: "codex", label: "Codex", logoKey: "codex", installed: true, configPresent: true, mcpWritable: true },
+ { harness: "claude", label: "Claude", logoKey: "claude", installed: true, configPresent: true, mcpWritable: true },
+ { harness: "cursor", label: "Cursor", logoKey: "cursor", installed: true, configPresent: true, mcpWritable: true },
+ {
+ harness: "openclaw",
+ label: "OpenClaw",
+ logoKey: "openclaw",
+ installed: true,
+ configPresent: true,
+ mcpWritable: false,
+ },
+ ];
+ const bindings: McpBindingDto[] = [
+ { harness: "codex", state: "managed" },
+ { harness: "claude", state: "missing" },
+ ];
+
+ it("renders only active writable harnesses by default", () => {
+ const { container } = render(
+ ,
+ );
+
+ const stackItems = Array.from(container.querySelectorAll(".harness-stack__item"));
+
+ expect(stackItems).toHaveLength(1);
+ expect(stackItems[0]).toHaveAttribute("data-state", "enabled");
+ expect(container.querySelector(".skill-card__harness-count")).toHaveTextContent("1/3");
+ });
+
+ it("renders every writable harness and marks missing bindings as disabled when requested", () => {
+ const { container } = render(
+ ,
+ );
+
+ const stackItems = Array.from(container.querySelectorAll(".harness-stack__item"));
+
+ expect(stackItems).toHaveLength(3);
+ expect(stackItems.map((item) => item.getAttribute("data-state"))).toEqual([
+ "enabled",
+ "disabled",
+ "disabled",
+ ]);
+ expect(container.querySelector(".skill-card__harness-count")).toHaveTextContent("1/3");
+ });
+});
diff --git a/frontend/src/features/mcp/components/McpHarnessLogoStack.tsx b/frontend/src/features/mcp/components/McpHarnessLogoStack.tsx
index e91a490..d898e06 100644
--- a/frontend/src/features/mcp/components/McpHarnessLogoStack.tsx
+++ b/frontend/src/features/mcp/components/McpHarnessLogoStack.tsx
@@ -6,24 +6,30 @@ import { isMcpHarnessAddressable } from "../model/selectors";
interface McpHarnessLogoStackProps {
bindings: McpBindingDto[];
columns: McpInventoryColumnDto[];
+ showAllWritable?: boolean;
}
/**
* Stack of harness logos for one MCP server.
- * - Shows logos for harnesses where state is `managed` or `drifted`,
- * restricted to harnesses with verified MCP write capability
- * (isMcpHarnessAddressable).
+ * - By default, shows writable harnesses where state is `managed` or `drifted`.
+ * - In MCP server cards, `showAllWritable` also shows writable missing
+ * harnesses as disabled so the card mirrors the Skills in-use coverage UI.
* - Different-config entries get an orange dot overlay (CSS via data-drifted).
* - Trailing "X/N" count = managed / addressable.
*/
-export function McpHarnessLogoStack({ bindings, columns }: McpHarnessLogoStackProps) {
+export function McpHarnessLogoStack({ bindings, columns, showAllWritable = false }: McpHarnessLogoStackProps) {
+ const bindingByHarness = new Map(bindings.map((binding) => [binding.harness, binding]));
const labelByHarness = new Map(columns.map((c) => [c.harness, c.label]));
const logoByHarness = new Map(columns.map((c) => [c.harness, c.logoKey ?? c.harness]));
- const addressable = new Set(columns.filter(isMcpHarnessAddressable).map((c) => c.harness));
+ const addressableColumns = columns.filter(isMcpHarnessAddressable);
+ const addressable = new Set(addressableColumns.map((c) => c.harness));
+ const visibleColumns = showAllWritable
+ ? addressableColumns
+ : addressableColumns.filter((column) => {
+ const state = bindingByHarness.get(column.harness)?.state;
+ return state === "managed" || state === "drifted";
+ });
- const visible = bindings.filter(
- (b) => addressable.has(b.harness) && (b.state === "managed" || b.state === "drifted"),
- );
const managedCount = bindings.filter(
(b) => addressable.has(b.harness) && b.state === "managed",
).length;
@@ -33,19 +39,24 @@ export function McpHarnessLogoStack({ bindings, columns }: McpHarnessLogoStackPr
return (