add-skill 1.0.24 → 1.0.25

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.
Files changed (3) hide show
  1. package/README.md +35 -18
  2. package/dist/index.js +241 -93
  3. package/package.json +21 -3
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Install agent skills onto your coding agents from any git repository.
4
4
 
5
5
  <!-- agent-list:start -->
6
- Supports **Opencode**, **Claude Code**, **Codex**, **Cursor**, and [13 more](#available-agents).
6
+ Supports **Opencode**, **Claude Code**, **Codex**, **Cursor**, and [19 more](#available-agents).
7
7
  <!-- agent-list:end -->
8
8
 
9
9
  ## Quick Start
@@ -17,6 +17,7 @@ npx add-skill vercel-labs/agent-skills
17
17
  Agent skills are reusable instruction sets that extend your coding agent's capabilities. They're defined in `SKILL.md` files with YAML frontmatter containing a `name` and `description`.
18
18
 
19
19
  Skills let agents perform specialized tasks like:
20
+
20
21
  - Generating release notes from git history
21
22
  - Creating PRs following your team's conventions
22
23
  - Integrating with external tools (Linear, Notion, etc.)
@@ -46,15 +47,15 @@ npx add-skill git@github.com:vercel-labs/agent-skills.git
46
47
 
47
48
  ### Options
48
49
 
49
- | Option | Description |
50
- |--------|-------------|
51
- | `-g, --global` | Install to user directory instead of project |
50
+ | Option | Description |
51
+ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
52
+ | `-g, --global` | Install to user directory instead of project |
52
53
  | `-a, --agent <agents...>` | <!-- agent-names:start -->Target specific agents (e.g., `claude-code`, `codex`). See [Available Agents](#available-agents)<!-- agent-names:end --> |
53
- | `-s, --skill <skills...>` | Install specific skills by name |
54
- | `-l, --list` | List available skills without installing |
55
- | `-y, --yes` | Skip all confirmation prompts |
56
- | `-V, --version` | Show version number |
57
- | `-h, --help` | Show help |
54
+ | `-s, --skill <skills...>` | Install specific skills by name |
55
+ | `-l, --list` | List available skills without installing |
56
+ | `-y, --yes` | Skip all confirmation prompts |
57
+ | `-V, --version` | Show version number |
58
+ | `-h, --help` | Show help |
58
59
 
59
60
  ### Examples
60
61
 
@@ -86,7 +87,9 @@ Skills can be installed to any of these supported agents. Use `-g, --global` to
86
87
  | Antigravity | `antigravity` | `.agent/skills/` | `~/.gemini/antigravity/skills/` |
87
88
  | Claude Code | `claude-code` | `.claude/skills/` | `~/.claude/skills/` |
88
89
  | Clawdbot | `clawdbot` | `skills/` | `~/.clawdbot/skills/` |
90
+ | Cline | `cline` | `.cline/skills/` | `~/.cline/skills/` |
89
91
  | Codex | `codex` | `.codex/skills/` | `~/.codex/skills/` |
92
+ | Command Code | `command-code` | `.commandcode/skills/` | `~/.commandcode/skills/` |
90
93
  | Cursor | `cursor` | `.cursor/skills/` | `~/.cursor/skills/` |
91
94
  | Droid | `droid` | `.factory/skills/` | `~/.factory/skills/` |
92
95
  | Gemini CLI | `gemini-cli` | `.gemini/skills/` | `~/.gemini/skills/` |
@@ -95,19 +98,22 @@ Skills can be installed to any of these supported agents. Use `-g, --global` to
95
98
  | Kilo Code | `kilo` | `.kilocode/skills/` | `~/.kilocode/skills/` |
96
99
  | Kiro CLI | `kiro-cli` | `.kiro/skills/` | `~/.kiro/skills/` |
97
100
  | OpenCode | `opencode` | `.opencode/skills/` | `~/.config/opencode/skills/` |
101
+ | OpenHands | `openhands` | `.openhands/skills/` | `~/.openhands/skills/` |
102
+ | Pi | `pi` | `.pi/skills/` | `~/.pi/agent/skills/` |
103
+ | Qoder | `qoder` | `.qoder/skills/` | `~/.qoder/skills/` |
98
104
  | Roo Code | `roo` | `.roo/skills/` | `~/.roo/skills/` |
99
105
  | Trae | `trae` | `.trae/skills/` | `~/.trae/skills/` |
100
106
  | Windsurf | `windsurf` | `.windsurf/skills/` | `~/.codeium/windsurf/skills/` |
107
+ | Zencoder | `zencoder` | `.zencoder/skills/` | `~/.zencoder/skills/` |
101
108
  | Neovate | `neovate` | `.neovate/skills/` | `~/.neovate/skills/` |
102
109
  <!-- available-agents:end -->
103
110
 
104
111
  > [!NOTE]
105
112
  > **Kiro CLI users:** After installing skills, you need to manually add them to your custom agent's `resources` in `.kiro/agents/<agent>.json`:
113
+ >
106
114
  > ```json
107
115
  > {
108
- > "resources": [
109
- > "skill://.kiro/skills/**/SKILL.md"
110
- > ]
116
+ > "resources": ["skill://.kiro/skills/**/SKILL.md"]
111
117
  > }
112
118
  > ```
113
119
 
@@ -158,7 +164,9 @@ The CLI searches for skills in these locations within a repository:
158
164
  - `.agent/skills/`
159
165
  - `.claude/skills/`
160
166
  - `./skills/`
167
+ - `.cline/skills/`
161
168
  - `.codex/skills/`
169
+ - `.commandcode/skills/`
162
170
  - `.cursor/skills/`
163
171
  - `.factory/skills/`
164
172
  - `.gemini/skills/`
@@ -167,9 +175,13 @@ The CLI searches for skills in these locations within a repository:
167
175
  - `.kilocode/skills/`
168
176
  - `.kiro/skills/`
169
177
  - `.opencode/skills/`
178
+ - `.openhands/skills/`
179
+ - `.pi/skills/`
180
+ - `.qoder/skills/`
170
181
  - `.roo/skills/`
171
182
  - `.trae/skills/`
172
183
  - `.windsurf/skills/`
184
+ - `.zencoder/skills/`
173
185
  - `.neovate/skills/`
174
186
  <!-- skill-discovery:end -->
175
187
 
@@ -179,12 +191,12 @@ If no skills are found in standard locations, a recursive search is performed.
179
191
 
180
192
  Skills are generally compatible across agents since they follow a shared [Agent Skills specification](https://agentskills.io). However, some features may be agent-specific:
181
193
 
182
- | Feature | OpenCode | Claude Code | Codex | Kiro CLI | Cursor | Antigravity | Roo Code | Github Copilot | Amp | Clawdbot | Neovate |
183
- |---------|----------|-------------|-------|----------|--------|-------------|----------|----------------|-----|----------|---------|
184
- | Basic skills | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
185
- | `allowed-tools` | Yes | Yes | Yes | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
186
- | `context: fork` | No | Yes | No | No | No | No | No | No | No | No | No |
187
- | Hooks | No | Yes | No | No | No | No | No | No | No | No | No |
194
+ | Feature | OpenCode | OpenHands | Claude Code | Cline | Codex | Command Code | Kiro CLI | Cursor | Antigravity | Roo Code | Github Copilot | Amp | Clawdbot | Neovate | Pi | Qoder | Zencoder |
195
+ | --------------- | -------- | --------- | ----------- | ----- | ----- | ------------ | -------- | ------ | ----------- | -------- | -------------- | --- | -------- | ------- | --- | ----- | -------- |
196
+ | Basic skills | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
197
+ | `allowed-tools` | Yes | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No |
198
+ | `context: fork` | No | No | Yes | No | No | No | No | No | No | No | No | No | No | No | No | No | No |
199
+ | Hooks | No | No | Yes | Yes | No | No | No | No | No | No | No | No | No | No | No | No | No |
188
200
 
189
201
  ## Troubleshooting
190
202
 
@@ -223,12 +235,17 @@ Telemetry is also automatically disabled in CI environments.
223
235
  - [Antigravity Skills Documentation](https://antigravity.google/docs/skills)
224
236
  - [Claude Code Skills Documentation](https://code.claude.com/docs/en/skills)
225
237
  - [Clawdbot Skills Documentation](https://docs.clawd.bot/tools/skills)
238
+ - [Cline Skills Documentation](https://docs.cline.bot/features/skills)
226
239
  - [Codex Skills Documentation](https://developers.openai.com/codex/skills)
240
+ - [Command Code Skills Documentation](https://commandcode.ai/docs/skills)
227
241
  - [Cursor Skills Documentation](https://cursor.com/docs/context/skills)
228
242
  - [Gemini CLI Skills Documentation](https://geminicli.com/docs/cli/skills/)
229
243
  - [GitHub Copilot Agent Skills](https://docs.github.com/en/copilot/concepts/agents/about-agent-skills)
230
244
  - [Kiro CLI Skills Documentation](https://kiro.dev/docs/cli/custom-agents/configuration-reference/#skill-resources)
231
245
  - [OpenCode Skills Documentation](https://opencode.ai/docs/skills)
246
+ - [OpenHands Skills Documentation](https://docs.openhands.ai/modules/usage/how-to/using-skills)
247
+ - [Pi Skills Documentation](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/skills.md)
248
+ - [Qoder Skills Documentation](https://docs.qoder.com/cli/Skills)
232
249
  - [Roo Code Skills Documentation](https://docs.roocode.com/features/skills)
233
250
  - [Trae Skills Documentation](https://docs.trae.ai/ide/skills)
234
251
  - [Vercel Agent Skills Repository](https://github.com/vercel-labs/agent-skills)
package/dist/index.js CHANGED
@@ -54,9 +54,7 @@ function parseSource(input) {
54
54
  url: input
55
55
  };
56
56
  }
57
- const githubTreeWithPathMatch = input.match(
58
- /github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/
59
- );
57
+ const githubTreeWithPathMatch = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/);
60
58
  if (githubTreeWithPathMatch) {
61
59
  const [, owner, repo, ref, subpath] = githubTreeWithPathMatch;
62
60
  return {
@@ -66,9 +64,7 @@ function parseSource(input) {
66
64
  subpath
67
65
  };
68
66
  }
69
- const githubTreeMatch = input.match(
70
- /github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/
71
- );
67
+ const githubTreeMatch = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/);
72
68
  if (githubTreeMatch) {
73
69
  const [, owner, repo, ref] = githubTreeMatch;
74
70
  return {
@@ -98,9 +94,7 @@ function parseSource(input) {
98
94
  subpath
99
95
  };
100
96
  }
101
- const gitlabTreeMatch = input.match(
102
- /gitlab\.com\/([^/]+)\/([^/]+)\/-\/tree\/([^/]+)$/
103
- );
97
+ const gitlabTreeMatch = input.match(/gitlab\.com\/([^/]+)\/([^/]+)\/-\/tree\/([^/]+)$/);
104
98
  if (gitlabTreeMatch) {
105
99
  const [, owner, repo, ref] = gitlabTreeMatch;
106
100
  return {
@@ -223,15 +217,23 @@ async function discoverSkills(basePath, subpath) {
223
217
  join2(searchPath, ".agent/skills"),
224
218
  join2(searchPath, ".agents/skills"),
225
219
  join2(searchPath, ".claude/skills"),
220
+ join2(searchPath, ".cline/skills"),
226
221
  join2(searchPath, ".codex/skills"),
222
+ join2(searchPath, ".commandcode/skills"),
227
223
  join2(searchPath, ".cursor/skills"),
228
224
  join2(searchPath, ".github/skills"),
229
225
  join2(searchPath, ".goose/skills"),
230
226
  join2(searchPath, ".kilocode/skills"),
231
227
  join2(searchPath, ".kiro/skills"),
228
+ join2(searchPath, ".neovate/skills"),
232
229
  join2(searchPath, ".opencode/skills"),
230
+ join2(searchPath, ".openhands/skills"),
231
+ join2(searchPath, ".pi/skills"),
232
+ join2(searchPath, ".qoder/skills"),
233
233
  join2(searchPath, ".roo/skills"),
234
- join2(searchPath, ".trae/skills")
234
+ join2(searchPath, ".trae/skills"),
235
+ join2(searchPath, ".windsurf/skills"),
236
+ join2(searchPath, ".zencoder/skills")
235
237
  ];
236
238
  for (const dir of prioritySearchDirs) {
237
239
  try {
@@ -314,6 +316,15 @@ var agents = {
314
316
  return existsSync(join3(home, ".clawdbot"));
315
317
  }
316
318
  },
319
+ cline: {
320
+ name: "cline",
321
+ displayName: "Cline",
322
+ skillsDir: ".cline/skills",
323
+ globalSkillsDir: join3(home, ".cline/skills"),
324
+ detectInstalled: async () => {
325
+ return existsSync(join3(home, ".cline"));
326
+ }
327
+ },
317
328
  codex: {
318
329
  name: "codex",
319
330
  displayName: "Codex",
@@ -323,6 +334,15 @@ var agents = {
323
334
  return existsSync(join3(home, ".codex"));
324
335
  }
325
336
  },
337
+ "command-code": {
338
+ name: "command-code",
339
+ displayName: "Command Code",
340
+ skillsDir: ".commandcode/skills",
341
+ globalSkillsDir: join3(home, ".commandcode/skills"),
342
+ detectInstalled: async () => {
343
+ return existsSync(join3(home, ".commandcode"));
344
+ }
345
+ },
326
346
  cursor: {
327
347
  name: "cursor",
328
348
  displayName: "Cursor",
@@ -395,6 +415,33 @@ var agents = {
395
415
  return existsSync(join3(home, ".config/opencode")) || existsSync(join3(home, ".claude/skills"));
396
416
  }
397
417
  },
418
+ openhands: {
419
+ name: "openhands",
420
+ displayName: "OpenHands",
421
+ skillsDir: ".openhands/skills",
422
+ globalSkillsDir: join3(home, ".openhands/skills"),
423
+ detectInstalled: async () => {
424
+ return existsSync(join3(home, ".openhands"));
425
+ }
426
+ },
427
+ pi: {
428
+ name: "pi",
429
+ displayName: "Pi",
430
+ skillsDir: ".pi/skills",
431
+ globalSkillsDir: join3(home, ".pi/agent/skills"),
432
+ detectInstalled: async () => {
433
+ return existsSync(join3(home, ".pi/agent"));
434
+ }
435
+ },
436
+ qoder: {
437
+ name: "qoder",
438
+ displayName: "Qoder",
439
+ skillsDir: ".qoder/skills",
440
+ globalSkillsDir: join3(home, ".qoder/skills"),
441
+ detectInstalled: async () => {
442
+ return existsSync(join3(home, ".qoder"));
443
+ }
444
+ },
398
445
  roo: {
399
446
  name: "roo",
400
447
  displayName: "Roo Code",
@@ -422,6 +469,15 @@ var agents = {
422
469
  return existsSync(join3(home, ".codeium/windsurf"));
423
470
  }
424
471
  },
472
+ zencoder: {
473
+ name: "zencoder",
474
+ displayName: "Zencoder",
475
+ skillsDir: ".zencoder/skills",
476
+ globalSkillsDir: join3(home, ".zencoder/skills"),
477
+ detectInstalled: async () => {
478
+ return existsSync(join3(home, ".zencoder"));
479
+ }
480
+ },
425
481
  neovate: {
426
482
  name: "neovate",
427
483
  displayName: "Neovate",
@@ -567,10 +623,7 @@ async function installSkillForAgent(skill, agentType, options = {}) {
567
623
  };
568
624
  }
569
625
  }
570
- var EXCLUDE_FILES = /* @__PURE__ */ new Set([
571
- "README.md",
572
- "metadata.json"
573
- ]);
626
+ var EXCLUDE_FILES = /* @__PURE__ */ new Set(["README.md", "metadata.json"]);
574
627
  var isExcluded = (name) => {
575
628
  if (EXCLUDE_FILES.has(name)) return true;
576
629
  if (name.startsWith("_")) return true;
@@ -762,7 +815,7 @@ async function installRemoteSkillForAgent(skill, agentType, options = {}) {
762
815
  }
763
816
 
764
817
  // src/index.ts
765
- import { homedir as homedir3 } from "os";
818
+ import { homedir as homedir4 } from "os";
766
819
 
767
820
  // src/telemetry.ts
768
821
  var TELEMETRY_URL = "https://add-skill.vercel.sh/t";
@@ -998,10 +1051,57 @@ var huggingFaceProvider = new HuggingFaceProvider();
998
1051
  registerProvider(mintlifyProvider);
999
1052
  registerProvider(huggingFaceProvider);
1000
1053
 
1054
+ // src/skill-lock.ts
1055
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1056
+ import { join as join5, dirname as dirname2 } from "path";
1057
+ import { homedir as homedir3 } from "os";
1058
+ var AGENTS_DIR2 = ".agents";
1059
+ var LOCK_FILE = ".skill-lock.json";
1060
+ var CURRENT_VERSION = 1;
1061
+ function getSkillLockPath() {
1062
+ return join5(homedir3(), AGENTS_DIR2, LOCK_FILE);
1063
+ }
1064
+ async function readSkillLock() {
1065
+ const lockPath = getSkillLockPath();
1066
+ try {
1067
+ const content = await readFile2(lockPath, "utf-8");
1068
+ const parsed = JSON.parse(content);
1069
+ if (typeof parsed.version !== "number" || !parsed.skills) {
1070
+ return createEmptyLockFile();
1071
+ }
1072
+ return parsed;
1073
+ } catch (error) {
1074
+ return createEmptyLockFile();
1075
+ }
1076
+ }
1077
+ async function writeSkillLock(lock) {
1078
+ const lockPath = getSkillLockPath();
1079
+ await mkdir2(dirname2(lockPath), { recursive: true });
1080
+ const content = JSON.stringify(lock, null, 2);
1081
+ await writeFile2(lockPath, content, "utf-8");
1082
+ }
1083
+ async function addSkillToLock(skillName, entry) {
1084
+ const lock = await readSkillLock();
1085
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1086
+ const existingEntry = lock.skills[skillName];
1087
+ lock.skills[skillName] = {
1088
+ ...entry,
1089
+ installedAt: existingEntry?.installedAt ?? now,
1090
+ updatedAt: now
1091
+ };
1092
+ await writeSkillLock(lock);
1093
+ }
1094
+ function createEmptyLockFile() {
1095
+ return {
1096
+ version: CURRENT_VERSION,
1097
+ skills: {}
1098
+ };
1099
+ }
1100
+
1001
1101
  // package.json
1002
1102
  var package_default = {
1003
1103
  name: "add-skill",
1004
- version: "1.0.24",
1104
+ version: "1.0.25",
1005
1105
  description: "Install agent skills onto coding agents (OpenCode, Claude Code, Codex, Cursor)",
1006
1106
  type: "module",
1007
1107
  bin: {
@@ -1014,7 +1114,15 @@ var package_default = {
1014
1114
  scripts: {
1015
1115
  build: "tsup src/index.ts --format esm --dts --clean",
1016
1116
  dev: "tsx src/index.ts",
1017
- prepublishOnly: "npm run build"
1117
+ prepublishOnly: "npm run build",
1118
+ format: "prettier --write 'src/**/*.ts' 'scripts/**/*.ts'",
1119
+ "format:check": "prettier --check 'src/**/*.ts' 'scripts/**/*.ts'",
1120
+ prepare: "husky",
1121
+ test: "echo 'No tests yet'"
1122
+ },
1123
+ "lint-staged": {
1124
+ "src/**/*.ts": "prettier --write",
1125
+ "scripts/**/*.ts": "prettier --write"
1018
1126
  },
1019
1127
  keywords: [
1020
1128
  "cli",
@@ -1025,7 +1133,9 @@ var package_default = {
1025
1133
  "antigravity",
1026
1134
  "claude-code",
1027
1135
  "clawdbot",
1136
+ "cline",
1028
1137
  "codex",
1138
+ "command-code",
1029
1139
  "cursor",
1030
1140
  "droid",
1031
1141
  "gemini-cli",
@@ -1034,9 +1144,13 @@ var package_default = {
1034
1144
  "kilo",
1035
1145
  "kiro-cli",
1036
1146
  "opencode",
1147
+ "openhands",
1148
+ "pi",
1149
+ "qoder",
1037
1150
  "roo",
1038
1151
  "trae",
1039
1152
  "windsurf",
1153
+ "zencoder",
1040
1154
  "neovate"
1041
1155
  ],
1042
1156
  repository: {
@@ -1058,18 +1172,22 @@ var package_default = {
1058
1172
  },
1059
1173
  devDependencies: {
1060
1174
  "@types/node": "^22.10.0",
1175
+ husky: "^9.1.7",
1176
+ "lint-staged": "^16.2.7",
1177
+ prettier: "^3.8.1",
1061
1178
  tsup: "^8.3.5",
1062
1179
  tsx: "^4.19.2",
1063
1180
  typescript: "^5.7.2"
1064
1181
  },
1065
1182
  engines: {
1066
1183
  node: ">=18"
1067
- }
1184
+ },
1185
+ packageManager: "pnpm@10.17.1"
1068
1186
  };
1069
1187
 
1070
1188
  // src/index.ts
1071
1189
  function shortenPath(fullPath, cwd) {
1072
- const home2 = homedir3();
1190
+ const home2 = homedir4();
1073
1191
  if (fullPath.startsWith(home2)) {
1074
1192
  return fullPath.replace(home2, "~");
1075
1193
  }
@@ -1089,23 +1207,14 @@ function formatList(items, maxShow = 5) {
1089
1207
  var version = package_default.version;
1090
1208
  setVersion(version);
1091
1209
  program.name("add-skill").description(
1092
- "Install skills onto coding agents (OpenCode, Claude Code, Codex, Cursor, Antigravity, Github Copilot, Roo Code)"
1210
+ "Install skills onto coding agents (OpenCode, Claude Code, Cline, Codex, Cursor, and more)"
1093
1211
  ).version(version).argument(
1094
1212
  "<source>",
1095
1213
  "Git repo URL, GitHub shorthand (owner/repo), local path (./path), or direct path to skill"
1096
- ).option(
1097
- "-g, --global",
1098
- "Install skill globally (user-level) instead of project-level"
1099
- ).option(
1214
+ ).option("-g, --global", "Install skill globally (user-level) instead of project-level").option(
1100
1215
  "-a, --agent <agents...>",
1101
- "Specify agents to install to (opencode, claude-code, codex, cursor, antigravity, gitub-copilot, roo)"
1102
- ).option(
1103
- "-s, --skill <skills...>",
1104
- "Specify skill names to install (skip selection prompt)"
1105
- ).option(
1106
- "-l, --list",
1107
- "List available skills in the repository without installing"
1108
- ).option("-y, --yes", "Skip confirmation prompts").option("--all", "Install all skills to all agents without any prompts (implies -y -g)").configureOutput({
1216
+ "Specify agents to install to (opencode, openhands, claude-code, cline, codex, cursor, and more)"
1217
+ ).option("-s, --skill <skills...>", "Specify skill names to install (skip selection prompt)").option("-l, --list", "List available skills in the repository without installing").option("-y, --yes", "Skip confirmation prompts").option("--all", "Install all skills to all agents without any prompts (implies -y -g)").configureOutput({
1109
1218
  outputError: (str, write) => {
1110
1219
  if (str.includes("missing required argument")) {
1111
1220
  console.log();
@@ -1145,9 +1254,7 @@ async function handleRemoteSkill(source, url, options, spinner2) {
1145
1254
  if (!providerSkill) {
1146
1255
  spinner2.stop(chalk.red("Invalid skill"));
1147
1256
  p.outro(
1148
- chalk.red(
1149
- "Could not fetch skill.md or missing required frontmatter (name, description)."
1150
- )
1257
+ chalk.red("Could not fetch skill.md or missing required frontmatter (name, description).")
1151
1258
  );
1152
1259
  process.exit(1);
1153
1260
  }
@@ -1171,9 +1278,7 @@ async function handleRemoteSkill(source, url, options, spinner2) {
1171
1278
  p.log.message(` ${chalk.cyan("Name:")} ${remoteSkill.name}`);
1172
1279
  p.log.message(` ${chalk.cyan("Install as:")} ${remoteSkill.installName}`);
1173
1280
  p.log.message(` ${chalk.cyan("Provider:")} ${provider.displayName}`);
1174
- p.log.message(
1175
- ` ${chalk.cyan("Description:")} ${remoteSkill.description}`
1176
- );
1281
+ p.log.message(` ${chalk.cyan("Description:")} ${remoteSkill.description}`);
1177
1282
  console.log();
1178
1283
  p.outro("Run without --list to install");
1179
1284
  process.exit(0);
@@ -1220,9 +1325,7 @@ async function handleRemoteSkill(source, url, options, spinner2) {
1220
1325
  targetAgents = installedAgents;
1221
1326
  if (installedAgents.length === 1) {
1222
1327
  const firstAgent = installedAgents[0];
1223
- p.log.info(
1224
- `Installing to: ${chalk.cyan(agents[firstAgent].displayName)}`
1225
- );
1328
+ p.log.info(`Installing to: ${chalk.cyan(agents[firstAgent].displayName)}`);
1226
1329
  } else {
1227
1330
  p.log.info(
1228
1331
  `Installing to: ${installedAgents.map((a) => chalk.cyan(agents[a].displayName)).join(", ")}`
@@ -1275,7 +1378,11 @@ async function handleRemoteSkill(source, url, options, spinner2) {
1275
1378
  const modeChoice = await p.select({
1276
1379
  message: "Installation method",
1277
1380
  options: [
1278
- { value: "symlink", label: "Symlink (Recommended)", hint: "Single source of truth, easy updates" },
1381
+ {
1382
+ value: "symlink",
1383
+ label: "Symlink (Recommended)",
1384
+ hint: "Single source of truth, easy updates"
1385
+ },
1279
1386
  { value: "copy", label: "Copy to all agents", hint: "Independent copies for each agent" }
1280
1387
  ]
1281
1388
  });
@@ -1347,6 +1454,16 @@ async function handleRemoteSkill(source, url, options, spinner2) {
1347
1454
  skillFiles: JSON.stringify({ [remoteSkill.installName]: url }),
1348
1455
  sourceType: remoteSkill.providerId
1349
1456
  });
1457
+ if (successful.length > 0 && installGlobally) {
1458
+ try {
1459
+ await addSkillToLock(remoteSkill.installName, {
1460
+ source: remoteSkill.sourceIdentifier,
1461
+ sourceType: remoteSkill.providerId,
1462
+ sourceUrl: url
1463
+ });
1464
+ } catch {
1465
+ }
1466
+ }
1350
1467
  if (successful.length > 0) {
1351
1468
  const resultLines = [];
1352
1469
  const firstResult = successful[0];
@@ -1380,16 +1497,18 @@ async function handleRemoteSkill(source, url, options, spinner2) {
1380
1497
  if (symlinkFailures.length > 0) {
1381
1498
  const copiedAgentNames = symlinkFailures.map((r) => r.agent);
1382
1499
  p.log.warn(chalk.yellow(`Symlinks failed for: ${formatList(copiedAgentNames)}`));
1383
- p.log.message(chalk.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
1500
+ p.log.message(
1501
+ chalk.dim(
1502
+ " Files were copied instead. On Windows, enable Developer Mode for symlink support."
1503
+ )
1504
+ );
1384
1505
  }
1385
1506
  }
1386
1507
  if (failed.length > 0) {
1387
1508
  console.log();
1388
1509
  p.log.error(chalk.red(`Failed to install ${failed.length}`));
1389
1510
  for (const r of failed) {
1390
- p.log.message(
1391
- ` ${chalk.red("\u2717")} ${r.skill} \u2192 ${r.agent}: ${chalk.dim(r.error)}`
1392
- );
1511
+ p.log.message(` ${chalk.red("\u2717")} ${r.skill} \u2192 ${r.agent}: ${chalk.dim(r.error)}`);
1393
1512
  }
1394
1513
  }
1395
1514
  console.log();
@@ -1416,9 +1535,7 @@ async function handleDirectUrlSkillLegacy(source, url, options, spinner2) {
1416
1535
  p.log.step(chalk.bold("Skill Details"));
1417
1536
  p.log.message(` ${chalk.cyan("Name:")} ${mintlifySkill.name}`);
1418
1537
  p.log.message(` ${chalk.cyan("Site:")} ${mintlifySkill.mintlifySite}`);
1419
- p.log.message(
1420
- ` ${chalk.cyan("Description:")} ${mintlifySkill.description}`
1421
- );
1538
+ p.log.message(` ${chalk.cyan("Description:")} ${mintlifySkill.description}`);
1422
1539
  console.log();
1423
1540
  p.outro("Run without --list to install");
1424
1541
  process.exit(0);
@@ -1465,9 +1582,7 @@ async function handleDirectUrlSkillLegacy(source, url, options, spinner2) {
1465
1582
  targetAgents = installedAgents;
1466
1583
  if (installedAgents.length === 1) {
1467
1584
  const firstAgent = installedAgents[0];
1468
- p.log.info(
1469
- `Installing to: ${chalk.cyan(agents[firstAgent].displayName)}`
1470
- );
1585
+ p.log.info(`Installing to: ${chalk.cyan(agents[firstAgent].displayName)}`);
1471
1586
  } else {
1472
1587
  p.log.info(
1473
1588
  `Installing to: ${installedAgents.map((a) => chalk.cyan(agents[a].displayName)).join(", ")}`
@@ -1520,7 +1635,11 @@ async function handleDirectUrlSkillLegacy(source, url, options, spinner2) {
1520
1635
  const modeChoice = await p.select({
1521
1636
  message: "Installation method",
1522
1637
  options: [
1523
- { value: "symlink", label: "Symlink (Recommended)", hint: "Single source of truth, easy updates" },
1638
+ {
1639
+ value: "symlink",
1640
+ label: "Symlink (Recommended)",
1641
+ hint: "Single source of truth, easy updates"
1642
+ },
1524
1643
  { value: "copy", label: "Copy to all agents", hint: "Independent copies for each agent" }
1525
1644
  ]
1526
1645
  });
@@ -1592,11 +1711,23 @@ async function handleDirectUrlSkillLegacy(source, url, options, spinner2) {
1592
1711
  skillFiles: JSON.stringify({ [mintlifySkill.mintlifySite]: url }),
1593
1712
  sourceType: "mintlify"
1594
1713
  });
1714
+ if (successful.length > 0 && installGlobally) {
1715
+ try {
1716
+ await addSkillToLock(mintlifySkill.mintlifySite, {
1717
+ source: `mintlify/${mintlifySkill.mintlifySite}`,
1718
+ sourceType: "mintlify",
1719
+ sourceUrl: url
1720
+ });
1721
+ } catch {
1722
+ }
1723
+ }
1595
1724
  if (successful.length > 0) {
1596
1725
  const resultLines = [];
1597
1726
  const firstResult = successful[0];
1598
1727
  if (firstResult.mode === "copy") {
1599
- resultLines.push(`${chalk.green("\u2713")} ${mintlifySkill.mintlifySite} ${chalk.dim("(copied)")}`);
1728
+ resultLines.push(
1729
+ `${chalk.green("\u2713")} ${mintlifySkill.mintlifySite} ${chalk.dim("(copied)")}`
1730
+ );
1600
1731
  for (const r of successful) {
1601
1732
  const shortPath = shortenPath(r.path, cwd);
1602
1733
  resultLines.push(` ${chalk.dim("\u2192")} ${shortPath}`);
@@ -1625,16 +1756,18 @@ async function handleDirectUrlSkillLegacy(source, url, options, spinner2) {
1625
1756
  if (symlinkFailures.length > 0) {
1626
1757
  const copiedAgentNames = symlinkFailures.map((r) => r.agent);
1627
1758
  p.log.warn(chalk.yellow(`Symlinks failed for: ${formatList(copiedAgentNames)}`));
1628
- p.log.message(chalk.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
1759
+ p.log.message(
1760
+ chalk.dim(
1761
+ " Files were copied instead. On Windows, enable Developer Mode for symlink support."
1762
+ )
1763
+ );
1629
1764
  }
1630
1765
  }
1631
1766
  if (failed.length > 0) {
1632
1767
  console.log();
1633
1768
  p.log.error(chalk.red(`Failed to install ${failed.length}`));
1634
1769
  for (const r of failed) {
1635
- p.log.message(
1636
- ` ${chalk.red("\u2717")} ${r.skill} \u2192 ${r.agent}: ${chalk.dim(r.error)}`
1637
- );
1770
+ p.log.message(` ${chalk.red("\u2717")} ${r.skill} \u2192 ${r.agent}: ${chalk.dim(r.error)}`);
1638
1771
  }
1639
1772
  }
1640
1773
  console.log();
@@ -1681,16 +1814,12 @@ async function main(source, options) {
1681
1814
  if (skills.length === 0) {
1682
1815
  spinner2.stop(chalk.red("No skills found"));
1683
1816
  p.outro(
1684
- chalk.red(
1685
- "No valid skills found. Skills require a SKILL.md with name and description."
1686
- )
1817
+ chalk.red("No valid skills found. Skills require a SKILL.md with name and description.")
1687
1818
  );
1688
1819
  await cleanup(tempDir);
1689
1820
  process.exit(1);
1690
1821
  }
1691
- spinner2.stop(
1692
- `Found ${chalk.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`
1693
- );
1822
+ spinner2.stop(`Found ${chalk.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
1694
1823
  if (options.list) {
1695
1824
  console.log();
1696
1825
  p.log.step(chalk.bold("Available Skills"));
@@ -1711,9 +1840,7 @@ async function main(source, options) {
1711
1840
  )
1712
1841
  );
1713
1842
  if (selectedSkills.length === 0) {
1714
- p.log.error(
1715
- `No matching skills found for: ${options.skill.join(", ")}`
1716
- );
1843
+ p.log.error(`No matching skills found for: ${options.skill.join(", ")}`);
1717
1844
  p.log.info("Available skills:");
1718
1845
  for (const s of skills) {
1719
1846
  p.log.message(` - ${getSkillDisplayName(s)}`);
@@ -1753,9 +1880,7 @@ async function main(source, options) {
1753
1880
  let targetAgents;
1754
1881
  const validAgents = Object.keys(agents);
1755
1882
  if (options.agent && options.agent.length > 0) {
1756
- const invalidAgents = options.agent.filter(
1757
- (a) => !validAgents.includes(a)
1758
- );
1883
+ const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
1759
1884
  if (invalidAgents.length > 0) {
1760
1885
  p.log.error(`Invalid agents: ${invalidAgents.join(", ")}`);
1761
1886
  p.log.info(`Valid agents: ${validAgents.join(", ")}`);
@@ -1777,15 +1902,11 @@ async function main(source, options) {
1777
1902
  targetAgents = validAgents;
1778
1903
  p.log.info("Installing to all agents (none detected)");
1779
1904
  } else {
1780
- p.log.warn(
1781
- "No coding agents detected. You can still install skills."
1782
- );
1783
- const allAgentChoices = Object.entries(agents).map(
1784
- ([key, config]) => ({
1785
- value: key,
1786
- label: config.displayName
1787
- })
1788
- );
1905
+ p.log.warn("No coding agents detected. You can still install skills.");
1906
+ const allAgentChoices = Object.entries(agents).map(([key, config]) => ({
1907
+ value: key,
1908
+ label: config.displayName
1909
+ }));
1789
1910
  const selected = await p.multiselect({
1790
1911
  message: "Select agents to install skills to",
1791
1912
  options: allAgentChoices,
@@ -1803,9 +1924,7 @@ async function main(source, options) {
1803
1924
  targetAgents = installedAgents;
1804
1925
  if (installedAgents.length === 1) {
1805
1926
  const firstAgent = installedAgents[0];
1806
- p.log.info(
1807
- `Installing to: ${chalk.cyan(agents[firstAgent].displayName)}`
1808
- );
1927
+ p.log.info(`Installing to: ${chalk.cyan(agents[firstAgent].displayName)}`);
1809
1928
  } else {
1810
1929
  p.log.info(
1811
1930
  `Installing to: ${installedAgents.map((a) => chalk.cyan(agents[a].displayName)).join(", ")}`
@@ -1860,7 +1979,11 @@ async function main(source, options) {
1860
1979
  const modeChoice = await p.select({
1861
1980
  message: "Installation method",
1862
1981
  options: [
1863
- { value: "symlink", label: "Symlink (Recommended)", hint: "Single source of truth, easy updates" },
1982
+ {
1983
+ value: "symlink",
1984
+ label: "Symlink (Recommended)",
1985
+ hint: "Single source of truth, easy updates"
1986
+ },
1864
1987
  { value: "copy", label: "Copy to all agents", hint: "Independent copies for each agent" }
1865
1988
  ]
1866
1989
  });
@@ -1877,7 +2000,10 @@ async function main(source, options) {
1877
2000
  for (const skill of selectedSkills) {
1878
2001
  const agentStatus = /* @__PURE__ */ new Map();
1879
2002
  for (const agent of targetAgents) {
1880
- agentStatus.set(agent, await isSkillInstalled(skill.name, agent, { global: installGlobally }));
2003
+ agentStatus.set(
2004
+ agent,
2005
+ await isSkillInstalled(skill.name, agent, { global: installGlobally })
2006
+ );
1881
2007
  }
1882
2008
  overwriteStatus.set(skill.name, agentStatus);
1883
2009
  }
@@ -1918,7 +2044,10 @@ async function main(source, options) {
1918
2044
  const results = [];
1919
2045
  for (const skill of selectedSkills) {
1920
2046
  for (const agent of targetAgents) {
1921
- const result = await installSkillForAgent(skill, agent, { global: installGlobally, mode: installMode });
2047
+ const result = await installSkillForAgent(skill, agent, {
2048
+ global: installGlobally,
2049
+ mode: installMode
2050
+ });
1922
2051
  results.push({
1923
2052
  skill: getSkillDisplayName(skill),
1924
2053
  agent: agents[agent].displayName,
@@ -1953,6 +2082,23 @@ async function main(source, options) {
1953
2082
  skillFiles: JSON.stringify(skillFiles)
1954
2083
  });
1955
2084
  }
2085
+ if (successful.length > 0 && installGlobally && normalizedSource) {
2086
+ const successfulSkillNames = new Set(successful.map((r) => r.skill));
2087
+ for (const skill of selectedSkills) {
2088
+ const skillDisplayName = getSkillDisplayName(skill);
2089
+ if (successfulSkillNames.has(skillDisplayName)) {
2090
+ try {
2091
+ await addSkillToLock(skill.name, {
2092
+ source: normalizedSource,
2093
+ sourceType: parsed.type,
2094
+ sourceUrl: parsed.url,
2095
+ skillPath: skillFiles[skill.name]
2096
+ });
2097
+ } catch {
2098
+ }
2099
+ }
2100
+ }
2101
+ }
1956
2102
  if (successful.length > 0) {
1957
2103
  const bySkill = /* @__PURE__ */ new Map();
1958
2104
  for (const r of successful) {
@@ -1988,28 +2134,30 @@ async function main(source, options) {
1988
2134
  }
1989
2135
  }
1990
2136
  }
1991
- const title = chalk.green(`Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""} to ${agentCount} agent${agentCount !== 1 ? "s" : ""}`);
2137
+ const title = chalk.green(
2138
+ `Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""} to ${agentCount} agent${agentCount !== 1 ? "s" : ""}`
2139
+ );
1992
2140
  p.note(resultLines.join("\n"), title);
1993
2141
  if (symlinkFailures.length > 0) {
1994
2142
  p.log.warn(chalk.yellow(`Symlinks failed for: ${formatList(copiedAgents)}`));
1995
- p.log.message(chalk.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
2143
+ p.log.message(
2144
+ chalk.dim(
2145
+ " Files were copied instead. On Windows, enable Developer Mode for symlink support."
2146
+ )
2147
+ );
1996
2148
  }
1997
2149
  }
1998
2150
  if (failed.length > 0) {
1999
2151
  console.log();
2000
2152
  p.log.error(chalk.red(`Failed to install ${failed.length}`));
2001
2153
  for (const r of failed) {
2002
- p.log.message(
2003
- ` ${chalk.red("\u2717")} ${r.skill} \u2192 ${r.agent}: ${chalk.dim(r.error)}`
2004
- );
2154
+ p.log.message(` ${chalk.red("\u2717")} ${r.skill} \u2192 ${r.agent}: ${chalk.dim(r.error)}`);
2005
2155
  }
2006
2156
  }
2007
2157
  console.log();
2008
2158
  p.outro(chalk.green("Done!"));
2009
2159
  } catch (error) {
2010
- p.log.error(
2011
- error instanceof Error ? error.message : "Unknown error occurred"
2012
- );
2160
+ p.log.error(error instanceof Error ? error.message : "Unknown error occurred");
2013
2161
  p.outro(chalk.red("Installation failed"));
2014
2162
  process.exit(1);
2015
2163
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "add-skill",
3
- "version": "1.0.24",
3
+ "version": "1.0.25",
4
4
  "description": "Install agent skills onto coding agents (OpenCode, Claude Code, Codex, Cursor)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,7 +13,15 @@
13
13
  "scripts": {
14
14
  "build": "tsup src/index.ts --format esm --dts --clean",
15
15
  "dev": "tsx src/index.ts",
16
- "prepublishOnly": "npm run build"
16
+ "prepublishOnly": "npm run build",
17
+ "format": "prettier --write 'src/**/*.ts' 'scripts/**/*.ts'",
18
+ "format:check": "prettier --check 'src/**/*.ts' 'scripts/**/*.ts'",
19
+ "prepare": "husky",
20
+ "test": "echo 'No tests yet'"
21
+ },
22
+ "lint-staged": {
23
+ "src/**/*.ts": "prettier --write",
24
+ "scripts/**/*.ts": "prettier --write"
17
25
  },
18
26
  "keywords": [
19
27
  "cli",
@@ -24,7 +32,9 @@
24
32
  "antigravity",
25
33
  "claude-code",
26
34
  "clawdbot",
35
+ "cline",
27
36
  "codex",
37
+ "command-code",
28
38
  "cursor",
29
39
  "droid",
30
40
  "gemini-cli",
@@ -33,9 +43,13 @@
33
43
  "kilo",
34
44
  "kiro-cli",
35
45
  "opencode",
46
+ "openhands",
47
+ "pi",
48
+ "qoder",
36
49
  "roo",
37
50
  "trae",
38
51
  "windsurf",
52
+ "zencoder",
39
53
  "neovate"
40
54
  ],
41
55
  "repository": {
@@ -57,11 +71,15 @@
57
71
  },
58
72
  "devDependencies": {
59
73
  "@types/node": "^22.10.0",
74
+ "husky": "^9.1.7",
75
+ "lint-staged": "^16.2.7",
76
+ "prettier": "^3.8.1",
60
77
  "tsup": "^8.3.5",
61
78
  "tsx": "^4.19.2",
62
79
  "typescript": "^5.7.2"
63
80
  },
64
81
  "engines": {
65
82
  "node": ">=18"
66
- }
83
+ },
84
+ "packageManager": "pnpm@10.17.1"
67
85
  }