@tec-explorer/skillpot 0.10.0 → 0.11.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  所有显著变更记录于此。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
4
4
  版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
5
5
 
6
+ ## [0.11.0] - 2026-09-04
7
+
8
+ ### Added
9
+ - **主线 C——落地策略**:B 档 copy 机制(适配器 `materialize: 'copy'`,enable 即重新拷贝刷新副本;doctor/台账/矩阵全链路兼容 copy)与**广播模式** `skillpot broadcast <skill> [--off]`(跨工具共享目录 `~/.agents/skills/`,显式开关、台账化)
10
+ - **主线 D——skills.sh 目录集成**:`skillpot search <关键词>`(匿名 `/api/search`,带安装量,与官方 npx skills CLI 同源)与 `skillpot install-search <owner/repo/slug> [--for agents]`(克隆缓存解析子目录后安装)
11
+ - Agent 注册表扩至八家:新增 **Amp**(`~/.config/amp/skills/`,官方文档确认);Cursor 验证依据升级为官方文档;dsh verified 按调研结论如实降级
12
+ - 英文版 `README.en.md`(中英互链)
13
+
6
14
  ## [0.10.0] - 2026-09-04
7
15
 
8
16
  ### Added
package/README.en.md ADDED
@@ -0,0 +1,87 @@
1
+ # SkillPot
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
+ [![npm](https://img.shields.io/npm/v/@tec-explorer/skillpot)](https://www.npmjs.com/package/@tec-explorer/skillpot)
5
+ [![Node](https://img.shields.io/badge/node-%E2%89%A518-brightgreen.svg)](package.json)
6
+
7
+ **The skill manager for coding agents — install once, expose per agent, update once.**
8
+
9
+ > Coding agents (Claude Code, ZCode, Codex, OpenCode, Gemini CLI, DeepSeek CLI, Cursor, Amp…) have converged on the same `SKILL.md` open standard, but each discovers skills from its own directory: a skill installed into `~/.claude/skills` only works for Claude Code. SkillPot keeps every skill in one central store and exposes it per agent via symlinks — with a switch matrix, consistency doctor, security lint, and team alignment.
10
+
11
+ [中文文档](README.md) | 📖 [Feature guide with screenshots](docs/guide.md)
12
+
13
+ ![GUI switch matrix](docs/images/gui-matrix.png)
14
+
15
+ ## Why SkillPot
16
+
17
+ Registries and marketplaces (skills.sh, Anthropic marketplace) answer *"where do I find skills"* — they are the **upstream**. SkillPot answers *"where does it install, who sees it, how do I stop it, how do I update it"* — the **management layer**:
18
+
19
+ - **One central store** at `~/.skillpot/skills/` — a single source of truth with checksums and a lockfile
20
+ - **Per-agent switch matrix** — `config.yaml` drives a symlink sync engine; flip switches in the GUI, the TUI, or the CLI
21
+ - **8 agents**: Claude Code, ZCode, Codex CLI, OpenCode, Gemini CLI, DeepSeek CLI (dsh), Cursor, Amp
22
+ - **Doctor**: broken links, drift, shadowed names, orphaned links — `--fix` repairs automatically
23
+ - **Security lint** on install: frontmatter integrity + dangerous script patterns (`rm -rf`, `curl | sh`, credential access, data exfiltration…)
24
+ - **Adopt** existing skills scattered across agent directories (copy or move mode)
25
+ - **Update** git-sourced skills in place (symlinks keep working, no re-linking) with file-level diffs
26
+ - **Market**: browse and one-click install from built-in sources — Anthropic, Vercel, Superpowers, Matt Pocock — or any custom git repo; search the skills.sh directory anonymously
27
+ - **Team alignment**: commit a `.skillpot.yaml` manifest, teammates run `skillpot sync` to match it
28
+ - **MCP bridge**: any MCP-capable agent can consume the central store, still filtered by the switch matrix
29
+ - **Broadcast mode**: expose a skill to the cross-tool `~/.agents/skills/` shared directory on demand
30
+
31
+ ## Quick start
32
+
33
+ Requires Node ≥ 18.
34
+
35
+ ```bash
36
+ npm install -g @tec-explorer/skillpot # or: npx @tec-explorer/skillpot (alias: spot)
37
+
38
+ skillpot init # create ~/.skillpot and detect installed agents
39
+ skillpot adopt --dry-run # preview: existing skills found in agent dirs
40
+ skillpot adopt --move # adopt with move mode (original dir becomes a symlink)
41
+ skillpot gui # web console: matrix / doctor / adopt / install / market / maintain / team
42
+ skillpot add ~/demo/my-skill # install a skill (exposed to no agent by default)
43
+ skillpot enable my-skill --for claude-code,codex
44
+ skillpot doctor # consistency check (--fix to repair)
45
+ skillpot search "commit message" # search the skills.sh directory
46
+ skillpot install-search anthropics/skills/pdf # install a directory result
47
+ skillpot sync # align with a project .skillpot.yaml manifest
48
+ ```
49
+
50
+ > Agents scan their skill directories at session start — restart a session after enable/disable.
51
+
52
+ ## How it works
53
+
54
+ ```
55
+ ~/.skillpot/
56
+ ├── skills/<name>/SKILL.md # central store: the single copy (self-contained, symlinks dereferenced)
57
+ ├── config.yaml # sources / checksums + the skill × agent switch matrix
58
+ ├── state.json # ledger of links this tool created (uninstall only touches these)
59
+ ├── skillspot.lock.json # machine-readable snapshot (team sharing / audit)
60
+ └── cache/market/ # clone cache for market sources
61
+ ```
62
+
63
+ `enable` creates a symlink from the agent's skills directory into the central store — agents discover it on their next session scan. `disable` removes it. The tool only ever touches paths recorded in its own ledger.
64
+
65
+ Landing strategies per agent: **A** symlink (default) → **B** copy + resync (agents that don't follow symlinks) → **C** MCP bridge (universal fallback).
66
+
67
+ ## Documentation
68
+
69
+ - [Feature guide (screenshots)](docs/guide.md) — Chinese, with screenshots of every surface
70
+ - [Design: agent adapters](docs/design/agent-adapters.md) · [Design: MCP bridge](docs/design/mcp-bridge.md) · [Product plan](docs/product/product-plan.md)
71
+
72
+ ## Security
73
+
74
+ Skills are instructions injected into model context plus optionally executable scripts. SkillPot's defaults: install lints before exposing, nothing is exposed to any agent until you say so, uninstall/disable only touches ledgered paths, and the web console listens on 127.0.0.1 with token-gated writes. Report vulnerabilities via [SECURITY.md](SECURITY.md).
75
+
76
+ ## Development
77
+
78
+ ```bash
79
+ npm install
80
+ npm test # vitest, sandboxed (never touches your real HOME)
81
+ npm run test:e2e # sandboxed end-to-end smoke
82
+ npm run build # typecheck + esbuild bundle + web GUI build
83
+ ```
84
+
85
+ ## License
86
+
87
+ [MIT](LICENSE)
package/README.md CHANGED
@@ -9,6 +9,8 @@
9
9
  **跨编程 Agent 的 Skill 管理器 —— 一处安装,按 Agent 开关,一处更新。**
10
10
  *Cross-agent skill manager for coding agents: install once, expose per agent, update once.*
11
11
 
12
+ [English](README.en.md) | 中文
13
+
12
14
  > 编程 Agent(Claude Code、ZCode、Codex、OpenCode、Gemini CLI、DeepSeek CLI、Cursor…)已收敛到同一套 `SKILL.md` 开放标准,但发现路径各自为政:装进 `~/.claude/skills` 就只对 Claude Code 生效。SkillPot 把 skill 收进一个中央仓库,按 Agent 粒度开关暴露,并提供冲突体检与安全扫描。
13
15
 
14
16
  ![GUI 开关矩阵](docs/images/gui-matrix.png)
package/dist/cli.mjs CHANGED
@@ -38684,7 +38684,7 @@ var AGENTS = [
38684
38684
  binaries: ["dsh"],
38685
38685
  fingerprints: (home) => [path2.join(home, ".dsh")],
38686
38686
  skillsDir: (home) => path2.join(home, ".dsh", "skills"),
38687
- verified: "\u76EE\u5F55\u7EA6\u5B9A\u540C Claude\uFF08~/.dsh/skills + SKILL.md\uFF09\uFF1B\u76EE\u5F55\u5185\u5DF2\u5B58\u5728\u7528\u6237\u624B\u5DE5\u521B\u5EFA\u5E76\u65E5\u5E38\u4F7F\u7528\u7684 symlink\uFF0C\u95F4\u63A5\u4F50\u8BC1 symlink \u53EF\u88AB\u53D1\u73B0\uFF1Bdsh CLI \u672C\u673A\u635F\u574F\uFF08@deepseek-ai/dsh \u6A21\u5757\u7F3A\u5931\uFF09\uFF0C\u5F85\u91CD\u88C5\u540E\u6700\u7EC8\u786E\u8BA4"
38687
+ verified: "\u76EE\u5F55\u7EA6\u5B9A\u540C Claude\uFF08~/.dsh/skills + SKILL.md\uFF09\uFF1B\u4F46 0.1.2-rc.1 \u4EE3\u7801\u672A\u89C1 skills \u8BFB\u53D6\u903B\u8F91\uFF08\u8BE5\u76EE\u5F55\u7684\u6D88\u8D39\u65B9\u5F85\u786E\u8BA4\uFF09\uFF0C\u7EB3\u7BA1\u4E3A\u524D\u77BB\u6027\u7EA6\u5B9A"
38688
38688
  },
38689
38689
  {
38690
38690
  id: "cursor",
@@ -38692,7 +38692,15 @@ var AGENTS = [
38692
38692
  binaries: ["cursor"],
38693
38693
  fingerprints: (home) => [path2.join(home, ".cursor")],
38694
38694
  skillsDir: (home) => path2.join(home, ".cursor", "skills"),
38695
- verified: "\u5B98\u65B9 create-skill \u6280\u80FD\u660E\u786E\uFF1A\u4E2A\u4EBA\u6280\u80FD\u76EE\u5F55\u4E3A ~/.cursor/skills/\uFF08\u9879\u76EE\u4E3A .cursor/skills/\uFF09\uFF1Bsymlink \u53D1\u73B0\u4EFB\u5F85\u5B9E\u673A\u786E\u8BA4"
38695
+ verified: "\u5B98\u65B9\u6587\u6863\u786E\u8BA4 ~/.cursor/skills/\uFF08\u4E2A\u4EBA\uFF09\u4E0E .cursor/skills/\uFF08\u9879\u76EE\uFF09\uFF1Bsymlink \u53D1\u73B0\u4EFB\u5F85\u5B9E\u673A\u786E\u8BA4"
38696
+ },
38697
+ {
38698
+ id: "amp",
38699
+ name: "Amp",
38700
+ binaries: ["amp"],
38701
+ fingerprints: (home) => [path2.join(home, ".config", "amp"), path2.join(home, ".amp")],
38702
+ skillsDir: (home) => path2.join(home, ".config", "amp", "skills"),
38703
+ verified: "\u5B98\u65B9\u6587\u6863\u786E\u8BA4 ~/.config/amp/skills/\uFF08\u7528\u6237\u7EA7\uFF0C\u591A\u76EE\u5F55\u5E76\u8BFB\uFF09\uFF1Bsymlink \u53D1\u73B0\u4EFB\u5F85\u5B9E\u673A\u786E\u8BA4"
38696
38704
  }
38697
38705
  ];
38698
38706
  function getAgent(id) {
@@ -38959,9 +38967,9 @@ function ledgerHas(state, skill, agent, linkPath) {
38959
38967
  (l) => l.skill === skill && l.agent === agent && l.link_path === linkPath
38960
38968
  );
38961
38969
  }
38962
- function addLedger(state, skill, agent, linkPath) {
38970
+ function addLedger(state, skill, agent, linkPath, kind = "symlink") {
38963
38971
  if (!ledgerHas(state, skill, agent, linkPath)) {
38964
- state.links.push({ skill, agent, link_path: linkPath });
38972
+ state.links.push({ skill, agent, link_path: linkPath, ...kind === "copy" ? { kind } : {} });
38965
38973
  }
38966
38974
  }
38967
38975
  function dropLedger(state, skill, agent) {
@@ -38972,6 +38980,13 @@ function linkTarget(agentId, skill) {
38972
38980
  if (!agent) throw new Error(`\u672A\u77E5 agent '${agentId}'`);
38973
38981
  return path7.join(agent.skillsDir(agentHome()), skill);
38974
38982
  }
38983
+ function materializeOf(agentId) {
38984
+ return getAgent(agentId)?.materialize ?? "symlink";
38985
+ }
38986
+ function copyInto(src, target) {
38987
+ fs5.mkdirSync(path7.dirname(target), { recursive: true });
38988
+ fs5.cpSync(src, target, { recursive: true, dereference: true });
38989
+ }
38975
38990
  function enableSkill(skill, agentIds) {
38976
38991
  const src = skillDir(skill);
38977
38992
  if (!fs5.existsSync(path7.join(src, "SKILL.md"))) {
@@ -38985,11 +39000,34 @@ function enableSkill(skill, agentIds) {
38985
39000
  const skipped = [];
38986
39001
  for (const agentId of agentIds) {
38987
39002
  const target = linkTarget(agentId, skill);
39003
+ const kind = materializeOf(agentId);
38988
39004
  let existing = null;
38989
39005
  try {
38990
39006
  existing = fs5.lstatSync(target);
38991
39007
  } catch {
38992
39008
  }
39009
+ if (kind === "copy") {
39010
+ if (existing) {
39011
+ if (ledgerHas(state, skill, agentId, target)) {
39012
+ fs5.rmSync(target, { recursive: true, force: true });
39013
+ copyInto(src, target);
39014
+ addLedger(state, skill, agentId, target, "copy");
39015
+ entry.expose[agentId] = true;
39016
+ linked.push(agentId);
39017
+ continue;
39018
+ }
39019
+ skipped.push({
39020
+ agent: agentId,
39021
+ reason: `${target} \u5DF2\u5B58\u5728\u771F\u5B9E${existing.isDirectory() ? "\u76EE\u5F55" : "\u6587\u4EF6"}\uFF08\u53EF\u80FD\u662F\u540C\u540D skill\uFF09\uFF0C\u62D2\u7EDD\u8986\u76D6`
39022
+ });
39023
+ continue;
39024
+ }
39025
+ copyInto(src, target);
39026
+ addLedger(state, skill, agentId, target, "copy");
39027
+ entry.expose[agentId] = true;
39028
+ linked.push(agentId);
39029
+ continue;
39030
+ }
38993
39031
  if (existing) {
38994
39032
  if (existing.isSymbolicLink()) {
38995
39033
  let resolved = null;
@@ -39053,6 +39091,13 @@ function disableSkill(skill, agentIds) {
39053
39091
  } catch {
39054
39092
  }
39055
39093
  if (st) {
39094
+ if (!st.isSymbolicLink() && ledgerHas(state, skill, agentId, target)) {
39095
+ fs5.rmSync(target, { recursive: true, force: true });
39096
+ dropLedger(state, skill, agentId);
39097
+ if (entry) entry.expose[agentId] = false;
39098
+ linked.push(agentId);
39099
+ continue;
39100
+ }
39056
39101
  if (st.isSymbolicLink()) {
39057
39102
  let pointsToStore = false;
39058
39103
  try {
@@ -39101,6 +39146,61 @@ function safeReadlink(p) {
39101
39146
  return "?";
39102
39147
  }
39103
39148
  }
39149
+ function broadcastSkill(skill, off = false) {
39150
+ const src = skillDir(skill);
39151
+ const state = loadState();
39152
+ const target = path7.join(agentHome(), ".agents", "skills", skill);
39153
+ if (off) {
39154
+ if (!fs5.existsSync(src)) throw new Error(`\u4E2D\u592E\u4ED3\u5E93\u4E2D\u627E\u4E0D\u5230 skill '${skill}'`);
39155
+ let st2 = null;
39156
+ try {
39157
+ st2 = fs5.lstatSync(target);
39158
+ } catch {
39159
+ }
39160
+ let pointsToStore = false;
39161
+ if (st2?.isSymbolicLink()) {
39162
+ try {
39163
+ pointsToStore = fs5.realpathSync(target) === fs5.realpathSync(src);
39164
+ } catch {
39165
+ pointsToStore = false;
39166
+ }
39167
+ }
39168
+ if (st2 && (pointsToStore || ledgerHas(state, skill, "broadcast", target))) {
39169
+ fs5.rmSync(target, { recursive: true, force: true });
39170
+ dropLedger(state, skill, "broadcast");
39171
+ saveState(state);
39172
+ return { changed: true, message: `'${skill}' \u5DF2\u64A4\u4E0B\u5E7F\u64AD\uFF08~/.agents/skills\uFF09` };
39173
+ }
39174
+ return { changed: false, message: "\u5E7F\u64AD\u76EE\u5F55\u4E2D\u6CA1\u6709\u8BE5 skill" };
39175
+ }
39176
+ if (!fs5.existsSync(path7.join(src, "SKILL.md"))) {
39177
+ throw new Error(`\u4E2D\u592E\u4ED3\u5E93\u4E2D\u627E\u4E0D\u5230 skill '${skill}'\uFF08${src}\uFF09`);
39178
+ }
39179
+ let st = null;
39180
+ try {
39181
+ st = fs5.lstatSync(target);
39182
+ } catch {
39183
+ }
39184
+ if (st) {
39185
+ let pointsToStore = false;
39186
+ if (st.isSymbolicLink()) {
39187
+ try {
39188
+ pointsToStore = fs5.realpathSync(target) === fs5.realpathSync(src);
39189
+ } catch {
39190
+ pointsToStore = false;
39191
+ }
39192
+ }
39193
+ if (pointsToStore || ledgerHas(state, skill, "broadcast", target)) {
39194
+ return { changed: false, message: `'${skill}' \u5DF2\u5728\u5E7F\u64AD\u4E2D` };
39195
+ }
39196
+ throw new Error(`${target} \u5DF2\u5B58\u5728\u4E14\u975E\u672C\u5DE5\u5177\u521B\u5EFA\u7684\u5E7F\u64AD\u94FE\u63A5\uFF0C\u62D2\u7EDD\u8986\u76D6`);
39197
+ }
39198
+ fs5.mkdirSync(path7.dirname(target), { recursive: true });
39199
+ fs5.symlinkSync(src, target, "dir");
39200
+ addLedger(state, skill, "broadcast", target);
39201
+ saveState(state);
39202
+ return { changed: true, message: `'${skill}' \u5DF2\u5E7F\u64AD\u5230 ~/.agents/skills\uFF08\u6240\u6709\u652F\u6301\u8BE5\u7EA6\u5B9A\u7684 Agent \u53EF\u89C1\uFF09` };
39203
+ }
39104
39204
 
39105
39205
  // src/core/lint.ts
39106
39206
  import fs6 from "node:fs";
@@ -39341,12 +39441,55 @@ async function installFromMarket(url, subdir, opts = {}) {
39341
39441
  }
39342
39442
  return addSkill(`${url}#${subdir}`, { name: opts.name, for: opts.for });
39343
39443
  }
39444
+ async function searchDirectory(query, limit = 20) {
39445
+ const q = query.trim();
39446
+ if (!q) throw new Error("\u641C\u7D22\u8BCD\u4E0D\u80FD\u4E3A\u7A7A");
39447
+ const res = await fetch(
39448
+ `https://skills.sh/api/search?q=${encodeURIComponent(q)}&limit=${Math.min(Math.max(limit, 1), 200)}`
39449
+ );
39450
+ if (!res.ok) throw new Error(`skills.sh \u641C\u7D22\u5931\u8D25\uFF1AHTTP ${res.status}`);
39451
+ const data = await res.json();
39452
+ return (data.skills ?? []).filter((s) => s.id && s.source).map((s) => ({
39453
+ id: s.id,
39454
+ name: s.name ?? s.id,
39455
+ source: s.source,
39456
+ installs: s.installs ?? 0
39457
+ }));
39458
+ }
39459
+ async function resolveDirectorySkill(id) {
39460
+ const parts = id.split("/").filter(Boolean);
39461
+ if (parts.length < 3) throw new Error(`id \u5F62\u5982 owner/repo/slug\uFF1A${id}`);
39462
+ const source = parts.slice(0, 2).join("/");
39463
+ const slug = parts.slice(2).join("/").toLowerCase();
39464
+ const repoUrl = `https://github.com/${source}.git`;
39465
+ const { skills } = await scanSource(repoUrl);
39466
+ const hit = matchDirectorySkill(skills, slug);
39467
+ if (!hit) {
39468
+ throw new Error(`\u5728 ${source} \u4E2D\u627E\u4E0D\u5230 skill '${slug}'\uFF08\u53EF\u8FD0\u884C skillpot market ${repoUrl} \u67E5\u770B\uFF09`);
39469
+ }
39470
+ return { repoUrl, subdir: hit.subdir };
39471
+ }
39472
+ function matchDirectorySkill(skills, slug) {
39473
+ const q = slug.toLowerCase();
39474
+ return skills.find((s) => s.name.toLowerCase() === q) ?? skills.find((s) => (s.subdir.split("/").pop() ?? "").toLowerCase() === q);
39475
+ }
39476
+ async function installFromDirectory(id, opts = {}) {
39477
+ const { repoUrl, subdir } = await resolveDirectorySkill(id);
39478
+ return installFromMarket(repoUrl, subdir, opts);
39479
+ }
39480
+ function formatInstalls(n) {
39481
+ if (!n || n <= 0) return "";
39482
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1).replace(/\.0$/, "")}M`;
39483
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1).replace(/\.0$/, "")}K`;
39484
+ return String(n);
39485
+ }
39344
39486
 
39345
39487
  // src/tui/matrix.ts
39346
39488
  import fs8 from "node:fs";
39347
39489
  import path11 from "node:path";
39348
39490
  function deriveMatrix(agents) {
39349
39491
  const config = loadConfig();
39492
+ const state = loadState();
39350
39493
  const skills = Object.keys(config.skills).sort();
39351
39494
  const list = agents ?? detectAll().map((r) => ({
39352
39495
  id: r.id,
@@ -39355,6 +39498,9 @@ function deriveMatrix(agents) {
39355
39498
  skillsDir: r.skillsDir
39356
39499
  }));
39357
39500
  const cells = {};
39501
+ const ledgeredCopy = (skill, agentId, target) => state.links.some(
39502
+ (l) => l.skill === skill && l.agent === agentId && l.link_path === target && l.kind === "copy"
39503
+ );
39358
39504
  for (const s of skills) {
39359
39505
  cells[s] = {};
39360
39506
  for (const a of list) {
@@ -39364,7 +39510,7 @@ function deriveMatrix(agents) {
39364
39510
  try {
39365
39511
  const st = fs8.lstatSync(target);
39366
39512
  actual = true;
39367
- managed = st.isSymbolicLink() && fs8.realpathSync(target) === fs8.realpathSync(skillDir(s));
39513
+ managed = ledgeredCopy(s, a.id, target) || st.isSymbolicLink() && fs8.realpathSync(target) === fs8.realpathSync(skillDir(s));
39368
39514
  } catch {
39369
39515
  }
39370
39516
  cells[s][a.id] = {
@@ -39628,6 +39774,16 @@ function runDoctor() {
39628
39774
  st = fs10.lstatSync(link2.link_path);
39629
39775
  } catch {
39630
39776
  }
39777
+ if (link2.kind === "copy") {
39778
+ if (!st || !st.isDirectory()) {
39779
+ issues.push({
39780
+ level: "error",
39781
+ message: `\u526F\u672C\u7F3A\u5931\uFF1A${link2.link_path}\uFF08${link2.skill} @ ${link2.agent}\uFF0C\u91CD\u65B0 enable \u53EF\u91CD\u5EFA\uFF09`,
39782
+ fix: "drop-ledger"
39783
+ });
39784
+ }
39785
+ continue;
39786
+ }
39631
39787
  if (!st) {
39632
39788
  issues.push({
39633
39789
  level: "error",
@@ -39707,7 +39863,11 @@ function fixDoctor() {
39707
39863
  state.links = state.links.filter((l) => {
39708
39864
  let ok = false;
39709
39865
  try {
39710
- ok = fs10.existsSync(l.link_path) && fs10.lstatSync(l.link_path).isSymbolicLink();
39866
+ if (l.kind === "copy") {
39867
+ ok = fs10.existsSync(l.link_path) && fs10.lstatSync(l.link_path).isDirectory();
39868
+ } else {
39869
+ ok = fs10.existsSync(l.link_path) && fs10.lstatSync(l.link_path).isSymbolicLink();
39870
+ }
39711
39871
  } catch {
39712
39872
  ok = false;
39713
39873
  }
@@ -46853,6 +47013,46 @@ program2.command("sync").description("\u56E2\u961F\u5BF9\u9F50\uFF1A\u6309\u9879
46853
47013
  if (errors.length) process.exitCode = 1;
46854
47014
  })
46855
47015
  );
47016
+ program2.command("broadcast <skill>").description("\u5E7F\u64AD\u6A21\u5F0F\uFF1A\u628A skill \u653E\u8FDB\u8DE8\u5DE5\u5177\u5171\u4EAB\u76EE\u5F55 ~/.agents/skills\uFF08\u7C97\u7C92\u5EA6\uFF0C\u6240\u6709\u652F\u6301\u8BE5\u7EA6\u5B9A\u7684 Agent \u53EF\u89C1\uFF09").option("--off", "\u64A4\u4E0B\u5E7F\u64AD").action(
47017
+ run((skill, opts) => {
47018
+ const r = broadcastSkill(skill, opts.off === true);
47019
+ if (r.changed) console.log(import_picocolors2.default.green(`\u2714 ${r.message}`));
47020
+ else console.log(import_picocolors2.default.dim(r.message));
47021
+ })
47022
+ );
47023
+ program2.command("search <query>").description("\u5728 skills.sh \u76EE\u5F55\u4E2D\u641C\u7D22 skill\uFF08\u533F\u540D\u8054\u7F51\uFF0C\u65E0\u9700 token\uFF09").option("--limit <n>", "\u7ED3\u679C\u6570\u91CF\u4E0A\u9650\uFF08\u9ED8\u8BA4 20\uFF09").action(
47024
+ run(async (query, opts) => {
47025
+ const skills = await searchDirectory(query, Number(opts.limit) || 20);
47026
+ if (!skills.length) {
47027
+ console.log(import_picocolors2.default.dim("\u65E0\u7ED3\u679C"));
47028
+ return;
47029
+ }
47030
+ console.log(
47031
+ renderTable(
47032
+ ["Skill", "\u5B89\u88C5\u91CF", "id"],
47033
+ skills.map((s) => [s.name, formatInstalls(s.installs), s.id])
47034
+ )
47035
+ );
47036
+ console.log(import_picocolors2.default.dim(`\u5B89\u88C5\uFF1Askillpot install-search <id>\uFF08\u4F8B\uFF1A${skills[0].id}\uFF09`));
47037
+ })
47038
+ );
47039
+ program2.command("install-search <id>").description("\u5B89\u88C5 skills.sh \u76EE\u5F55\u4E2D\u7684 skill\uFF08id \u5F62\u5982 owner/repo/slug\uFF09").option("-f, --for <agents>", "\u5B89\u88C5\u540E\u5F00\u653E\u7ED9\u6307\u5B9A Agent\uFF08\u9017\u53F7\u5206\u9694\u6216 all\uFF09").action(
47040
+ run(async (id, opts) => {
47041
+ const r = await installFromDirectory(id, {
47042
+ for: opts.for ? resolveAgentIds(opts.for) : void 0
47043
+ });
47044
+ console.log(import_picocolors2.default.green(`\u2714 \u5DF2\u5B89\u88C5 ${r.name}`));
47045
+ if (r.description) console.log(import_picocolors2.default.dim(` ${r.description.slice(0, 120)}`));
47046
+ for (const i of r.lint) {
47047
+ console.log(` ${i.level === "error" ? import_picocolors2.default.red("error") : import_picocolors2.default.yellow("warn ")} ${i.message}`);
47048
+ }
47049
+ if (r.enabled.length) console.log(import_picocolors2.default.green(`\u5DF2\u5F00\u653E\uFF1A${r.enabled.join(", ")}`));
47050
+ else
47051
+ console.log(
47052
+ import_picocolors2.default.dim("\u9ED8\u8BA4\u672A\u5F00\u653E\u3002skillpot enable <skill> --for <agents> \u6216\u5728 GUI \u77E9\u9635\u6253\u52FE")
47053
+ );
47054
+ })
47055
+ );
46856
47056
  for (const cmd of program2.commands) cmd.allowExcessArguments(false);
46857
47057
  await program2.parseAsync(process.argv);
46858
47058
  /*! Bundled license information:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tec-explorer/skillpot",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Cross-agent skill manager: install once, expose per coding agent (Claude Code / ZCode / Codex / OpenCode / Gemini CLI / DeepSeek CLI / Cursor).",
5
5
  "repository": {
6
6
  "type": "git",