skill-linker 4.1.0 → 4.1.2

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/README.md CHANGED
@@ -21,7 +21,7 @@
21
21
 
22
22
  ```bash
23
23
  # 安裝技能(需要 --skill 或 --from)
24
- npx /app/workspace/projects/skill-linker install --skill <路徑> --agent opencode --scope both --yes
24
+ npx skill-linker install --skill <路徑> --agent opencode --scope both --yes
25
25
  npx skill-linker install --from https://github.com/anthropics/skills --agent claude --scope both
26
26
 
27
27
  # 列出已安裝的 Repos
@@ -159,6 +159,8 @@ npx skill-linker install --from https://github.com/obra/superpowers --agent clau
159
159
 
160
160
  1. **權限問題**:在建立 Symlink 時,請確保您有對應目錄的寫入權限。
161
161
  2. **環境需求**:需安裝 Node.js 18.0.0 以上版本。
162
+ 3. **Windows**:建立 Symlink 需開啟「開發者模式」或以系統管理員權限執行,否則 `fs.symlinkSync` 會失敗。
163
+ 4. **覆寫保護**:`--yes` 只會覆寫既有的 Symlink;若目標是「真實目錄/檔案」,工具會拒絕刪除以保護資料。
162
164
 
163
165
  ## 授權
164
166
 
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "skill-linker",
3
- "version": "4.1.0",
3
+ "version": "4.1.2",
4
4
  "description": "CLI to link AI Agent Skills to various agents (Claude, Copilot, Antigravity, Cursor, etc.)",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
7
7
  "skill-linker": "bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "echo \"No tests yet\""
10
+ "test": "node --test"
11
11
  },
12
12
  "keywords": [
13
13
  "ai",
@@ -40,6 +40,7 @@
40
40
  "files": [
41
41
  "bin/",
42
42
  "src/",
43
+ "skills/",
43
44
  "README.md"
44
45
  ]
45
46
  }
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: skill-linker
3
+ description: Install and manage AI Agent Skills for Claude, Cursor, OpenCode, Gemini, Windsurf and other AI coding assistants. Use when you need to add new capabilities to your AI agent.
4
+ ---
5
+
6
+ # Skill: skill-linker
7
+
8
+ A CLI tool to install and manage AI Agent Skills.
9
+
10
+ ## When to Use This Skill
11
+
12
+ Use this skill when:
13
+
14
+ - Installing new AI Agent Skills from GitHub or local directories
15
+ - Adding capabilities to Claude, Cursor, OpenCode or other AI assistants
16
+ - Managing skill installations (project or global)
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ # Install from GitHub
22
+ npx skill-linker install --from <url> --agent <agent> --scope both
23
+
24
+ # Install from local directory
25
+ npx skill-linker install --skill <path> --agent <agent> --scope both --yes
26
+ ```
27
+
28
+ ## Supported Agents
29
+
30
+ | Agent | Skill Directory |
31
+ | -------- | ------------------- |
32
+ | opencode | `.opencode/skills/` |
33
+ | claude | `.claude/skills/` |
34
+ | cursor | `.cursor/skills/` |
35
+ | gemini | `.gemini/skills/` |
36
+ | windsurf | `.windsurf/skills/` |
37
+
38
+ ## Parameters
39
+
40
+ | Parameter | Description |
41
+ | ---------------- | --------------------------- |
42
+ | `--from <url>` | GitHub repository URL |
43
+ | `--skill <path>` | Local skill directory |
44
+ | `-a, --agent` | Target agent |
45
+ | `-s, --scope` | Scope (project/global/both) |
46
+ | `-y, --yes` | Auto overwrite |
47
+
48
+ ## Examples
49
+
50
+ ```bash
51
+ # Install for OpenCode
52
+ npx skill-linker install --from https://github.com/user/repo --agent opencode --scope both
53
+
54
+ # Install for Claude
55
+ npx skill-linker install --from https://github.com/org/skills --agent claude --scope project
56
+
57
+ # Update existing skill
58
+ npx skill-linker install --from https://github.com/org/skills --agent opencode --scope both --yes
59
+ ```
package/src/cli.js CHANGED
@@ -19,10 +19,7 @@ program
19
19
  program
20
20
  .command("install")
21
21
  .description("Install a skill to specified agents")
22
- .requiredOption(
23
- "--skill <path>",
24
- "Path to skill directory or --from clone URL",
25
- )
22
+ .option("--skill <path>", "Path to a local skill directory")
26
23
  .option("--from <github-url>", "Clone skill from GitHub URL first, then link")
27
24
  .option(
28
25
  "-a, --agent <names...>",
@@ -75,6 +75,15 @@ async function install(options) {
75
75
  skillPaths = [options.skill];
76
76
  }
77
77
 
78
+ // Require at least one source: --skill or --from
79
+ if (skillPaths.length === 0) {
80
+ console.error(
81
+ chalk.red("[ERROR]"),
82
+ "No skill source provided. Use --skill <path> or --from <github-url>.",
83
+ );
84
+ process.exit(1);
85
+ }
86
+
78
87
  // Validate skill paths
79
88
  for (const p of skillPaths) {
80
89
  if (!dirExists(p)) {
@@ -147,6 +156,9 @@ async function install(options) {
147
156
  }
148
157
  console.log(chalk.blue("[INFO]"), `Scope: ${scope}`);
149
158
 
159
+ let linkedCount = 0;
160
+ let failedCount = 0;
161
+
150
162
  // Process each selected agent
151
163
  for (const agentIndex of selectedAgents) {
152
164
  const agent = agents[agentIndex];
@@ -189,15 +201,27 @@ async function install(options) {
189
201
 
190
202
  if (createSymlink(sPath, targetLink)) {
191
203
  console.log(chalk.green("[SUCCESS]"), `Linked ${sName}`);
204
+ linkedCount++;
192
205
  } else {
193
206
  console.error(chalk.red("[ERROR]"), `Failed to link ${sName}`);
207
+ failedCount++;
194
208
  }
195
209
  }
196
210
  }
197
211
  }
198
212
 
199
213
  console.log("");
200
- console.log(chalk.green("[SUCCESS]"), "All operations completed.");
214
+ if (failedCount > 0) {
215
+ console.error(
216
+ chalk.red("[ERROR]"),
217
+ `Completed with errors: ${linkedCount} linked, ${failedCount} failed.`,
218
+ );
219
+ process.exit(1);
220
+ }
221
+ console.log(
222
+ chalk.green("[SUCCESS]"),
223
+ `All operations completed (${linkedCount} linked).`,
224
+ );
201
225
  }
202
226
 
203
227
  module.exports = install;
@@ -35,9 +35,27 @@ function createSymlink(source, target) {
35
35
  // Ensure parent directory exists
36
36
  ensureDir(path.dirname(target));
37
37
 
38
- // Remove existing link/file/directory if present
39
- // force: true makes it ignore the error if file doesn't exist
40
- fs.rmSync(target, { recursive: true, force: true });
38
+ // Inspect the target itself without following symlinks.
39
+ let stat = null;
40
+ try {
41
+ stat = fs.lstatSync(target);
42
+ } catch {
43
+ // Target does not exist — nothing to remove.
44
+ }
45
+
46
+ if (stat) {
47
+ if (stat.isSymbolicLink()) {
48
+ // Safe: we are replacing a link, not real content.
49
+ fs.rmSync(target, { force: true });
50
+ } else {
51
+ // A real file or directory lives here. Refuse to delete it so
52
+ // we never destroy user data that we did not create.
53
+ console.error(
54
+ `Refusing to overwrite non-symlink target: ${target}`,
55
+ );
56
+ return false;
57
+ }
58
+ }
41
59
 
42
60
  fs.symlinkSync(source, target, 'dir');
43
61
  return true;