any-skills 0.1.1 → 0.1.3
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 +57 -14
- package/package.json +3 -3
- package/scripts/cli.js +30 -0
- package/scripts/{postinstall.js → link-skills.js} +112 -30
package/README.md
CHANGED
|
@@ -1,22 +1,59 @@
|
|
|
1
1
|
# any-skills
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Create a shared skills directory and link tool-specific folders so Claude, Codex,
|
|
4
|
+
Gemini, and others can reuse the same skills.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
- `.codex/skills`
|
|
6
|
+
## What it does
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
- Creates a shared `.skills` directory in the working directory (or uses the
|
|
9
|
+
configured target).
|
|
10
|
+
- Creates tool links such as `.claude/skills` or `.codex/skills`.
|
|
11
|
+
- Auto-detects `claude`, `codex`, and `gemini` commands on your PATH by default.
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install any-skills --save-dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install globally:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install -g any-skills
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Create links in the current directory:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npx any-skills
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Create links in your home directory (for example `~/.claude/skills`):
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npx any-skills -g
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Limit to specific tools:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npx any-skills claude
|
|
43
|
+
npx any-skills deepseek
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If a tool is not in the configured map, it links to `.<name>/skills` by default.
|
|
11
47
|
|
|
12
48
|
## Configuration
|
|
13
49
|
|
|
14
|
-
|
|
50
|
+
Create `.skillsrc` (JSON) in the working directory.
|
|
15
51
|
|
|
16
52
|
Supported fields:
|
|
17
53
|
|
|
18
54
|
- `target`: optional string. Overrides where skills are stored (default: `.skills`).
|
|
19
55
|
- `links`: array of link definitions. Strings map to `target` by default.
|
|
56
|
+
- `tools`: optional object mapping command names to link paths.
|
|
20
57
|
|
|
21
58
|
Example:
|
|
22
59
|
|
|
@@ -27,22 +64,28 @@ Example:
|
|
|
27
64
|
}
|
|
28
65
|
```
|
|
29
66
|
|
|
67
|
+
Example (auto-detect commands):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"tools": {
|
|
72
|
+
"claude": ".claude/skills",
|
|
73
|
+
"codex": ".codex/skills",
|
|
74
|
+
"gemini": ".gemini/skills"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
30
79
|
## Cross-platform behavior
|
|
31
80
|
|
|
32
81
|
- macOS / Linux uses `dir` symlinks
|
|
33
82
|
- Windows uses `junction` for better compatibility
|
|
34
83
|
|
|
35
|
-
## Usage
|
|
36
|
-
|
|
37
|
-
```sh
|
|
38
|
-
npm install any-skills --save-dev
|
|
39
|
-
```
|
|
40
|
-
|
|
41
84
|
## Git ignore
|
|
42
85
|
|
|
43
86
|
Add the tool-specific shared skill directories to your `.gitignore`:
|
|
44
87
|
|
|
45
88
|
```
|
|
46
|
-
.claude
|
|
47
|
-
.codex
|
|
89
|
+
.claude/skills
|
|
90
|
+
.codex/skills
|
|
48
91
|
```
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "any-skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Share skills between Claude, Codex, and similar tools via a shared .skills directory.",
|
|
5
5
|
"type": "commonjs",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
6
|
+
"bin": {
|
|
7
|
+
"any-skills": "scripts/cli.js"
|
|
8
8
|
}
|
|
9
9
|
}
|
package/scripts/cli.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const { linkSkills } = require('./link-skills');
|
|
6
|
+
|
|
7
|
+
function parseArgs(argv) {
|
|
8
|
+
const flags = new Set();
|
|
9
|
+
const tools = [];
|
|
10
|
+
for (const arg of argv) {
|
|
11
|
+
if (arg === '--global' || arg === '-g') {
|
|
12
|
+
flags.add('global');
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (arg.startsWith('-')) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
tools.push(arg);
|
|
19
|
+
}
|
|
20
|
+
return { flags, tools };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const rootDir = process.cwd();
|
|
24
|
+
const { flags, tools } = parseArgs(process.argv.slice(2));
|
|
25
|
+
const linkRoot = flags.has('global') ? os.homedir() : rootDir;
|
|
26
|
+
const explicitTools = tools.length ? tools : null;
|
|
27
|
+
const exitCode = linkSkills(rootDir, { linkRoot, explicitTools });
|
|
28
|
+
if (exitCode) {
|
|
29
|
+
process.exit(exitCode);
|
|
30
|
+
}
|
|
@@ -3,24 +3,13 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
|
-
function getInstallRoot() {
|
|
7
|
-
const initCwd = process.env.INIT_CWD;
|
|
8
|
-
if (initCwd) {
|
|
9
|
-
return path.resolve(initCwd);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const npmPrefix = process.env.npm_config_local_prefix;
|
|
13
|
-
if (npmPrefix) {
|
|
14
|
-
return path.resolve(npmPrefix);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return process.cwd();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const rootDir = getInstallRoot();
|
|
21
6
|
const targetName = '.skills';
|
|
22
7
|
const configFileName = '.skillsrc';
|
|
23
|
-
const
|
|
8
|
+
const defaultToolTargets = {
|
|
9
|
+
claude: '.claude/skills',
|
|
10
|
+
codex: '.codex/skills',
|
|
11
|
+
gemini: '.gemini/skills',
|
|
12
|
+
};
|
|
24
13
|
|
|
25
14
|
function ensureDir(dirPath) {
|
|
26
15
|
if (!fs.existsSync(dirPath)) {
|
|
@@ -129,7 +118,7 @@ function resolveRootPath(value, baseDir) {
|
|
|
129
118
|
return path.isAbsolute(value) ? value : path.join(baseDir, value);
|
|
130
119
|
}
|
|
131
120
|
|
|
132
|
-
function resolveTarget(config) {
|
|
121
|
+
function resolveTarget(config, rootDir) {
|
|
133
122
|
if (
|
|
134
123
|
config &&
|
|
135
124
|
typeof config.target === 'string' &&
|
|
@@ -141,7 +130,7 @@ function resolveTarget(config) {
|
|
|
141
130
|
return path.join(rootDir, targetName);
|
|
142
131
|
}
|
|
143
132
|
|
|
144
|
-
function normalizeLinkEntry(entry, target, configPath) {
|
|
133
|
+
function normalizeLinkEntry(entry, target, configPath, rootDir) {
|
|
145
134
|
if (typeof entry === 'string') {
|
|
146
135
|
return { link: entry, error: false };
|
|
147
136
|
}
|
|
@@ -202,17 +191,108 @@ function getConfigEntries(config) {
|
|
|
202
191
|
return null;
|
|
203
192
|
}
|
|
204
193
|
|
|
205
|
-
function
|
|
194
|
+
function commandExists(command) {
|
|
195
|
+
const pathEnv = process.env.PATH || '';
|
|
196
|
+
const pathEntries = pathEnv.split(path.delimiter).filter(Boolean);
|
|
197
|
+
const isWindows = process.platform === 'win32';
|
|
198
|
+
const extensions = isWindows
|
|
199
|
+
? (process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
|
|
200
|
+
.split(';')
|
|
201
|
+
.filter(Boolean)
|
|
202
|
+
: [''];
|
|
203
|
+
|
|
204
|
+
for (const entry of pathEntries) {
|
|
205
|
+
for (const ext of extensions) {
|
|
206
|
+
const candidate = path.join(entry, `${command}${ext}`);
|
|
207
|
+
try {
|
|
208
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
209
|
+
return true;
|
|
210
|
+
} catch (err) {
|
|
211
|
+
if (err.code !== 'ENOENT') {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function detectLinkTargets(config) {
|
|
222
|
+
const toolTargets =
|
|
223
|
+
config && config.tools && typeof config.tools === 'object'
|
|
224
|
+
? config.tools
|
|
225
|
+
: defaultToolTargets;
|
|
226
|
+
|
|
227
|
+
const targets = [];
|
|
228
|
+
for (const [command, linkTarget] of Object.entries(toolTargets)) {
|
|
229
|
+
if (typeof linkTarget !== 'string' || linkTarget.trim() === '') {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (commandExists(command)) {
|
|
233
|
+
targets.push(linkTarget);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return targets;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function resolveExplicitTargets(config, toolNames) {
|
|
241
|
+
const toolTargets =
|
|
242
|
+
config && config.tools && typeof config.tools === 'object'
|
|
243
|
+
? config.tools
|
|
244
|
+
: defaultToolTargets;
|
|
245
|
+
|
|
246
|
+
const targets = [];
|
|
247
|
+
for (const name of toolNames) {
|
|
248
|
+
const mappedTarget = toolTargets[name];
|
|
249
|
+
if (typeof mappedTarget === 'string' && mappedTarget.trim() !== '') {
|
|
250
|
+
targets.push(mappedTarget);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const safeName = name.trim();
|
|
254
|
+
if (safeName) {
|
|
255
|
+
targets.push(`.${safeName}/skills`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return targets;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function buildLinkMappings({
|
|
263
|
+
config,
|
|
264
|
+
configPath,
|
|
265
|
+
exists,
|
|
266
|
+
target,
|
|
267
|
+
rootDir,
|
|
268
|
+
linkRoot,
|
|
269
|
+
explicitTools,
|
|
270
|
+
}) {
|
|
271
|
+
const resolvedLinkRoot = linkRoot || rootDir;
|
|
272
|
+
const explicitTargets = Array.isArray(explicitTools)
|
|
273
|
+
? resolveExplicitTargets(config, explicitTools)
|
|
274
|
+
: null;
|
|
206
275
|
const entries = getConfigEntries(config);
|
|
207
276
|
if (!entries) {
|
|
277
|
+
if (explicitTargets) {
|
|
278
|
+
return {
|
|
279
|
+
mappings: explicitTargets.map((linkTarget) => ({
|
|
280
|
+
linkPath: resolveRootPath(linkTarget, resolvedLinkRoot),
|
|
281
|
+
targetPath: target,
|
|
282
|
+
})),
|
|
283
|
+
error: false,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
208
287
|
if (exists) {
|
|
209
288
|
console.warn(
|
|
210
|
-
`[any-skills] No link configuration found in ${configPath}; using
|
|
289
|
+
`[any-skills] No link configuration found in ${configPath}; using auto-detected tools.`
|
|
211
290
|
);
|
|
212
291
|
}
|
|
292
|
+
const detectedTargets = detectLinkTargets(config);
|
|
213
293
|
return {
|
|
214
|
-
mappings:
|
|
215
|
-
linkPath: resolveRootPath(linkTarget,
|
|
294
|
+
mappings: detectedTargets.map((linkTarget) => ({
|
|
295
|
+
linkPath: resolveRootPath(linkTarget, resolvedLinkRoot),
|
|
216
296
|
targetPath: target,
|
|
217
297
|
})),
|
|
218
298
|
error: false,
|
|
@@ -225,7 +305,7 @@ function buildLinkMappings({ config, configPath, exists, target }) {
|
|
|
225
305
|
|
|
226
306
|
const mappings = [];
|
|
227
307
|
for (const entry of entries) {
|
|
228
|
-
const normalized = normalizeLinkEntry(entry, target, configPath);
|
|
308
|
+
const normalized = normalizeLinkEntry(entry, target, configPath, rootDir);
|
|
229
309
|
if (!normalized) {
|
|
230
310
|
continue;
|
|
231
311
|
}
|
|
@@ -233,7 +313,7 @@ function buildLinkMappings({ config, configPath, exists, target }) {
|
|
|
233
313
|
return { mappings: [], error: true };
|
|
234
314
|
}
|
|
235
315
|
mappings.push({
|
|
236
|
-
linkPath: resolveRootPath(normalized.link,
|
|
316
|
+
linkPath: resolveRootPath(normalized.link, resolvedLinkRoot),
|
|
237
317
|
targetPath: target,
|
|
238
318
|
});
|
|
239
319
|
}
|
|
@@ -241,7 +321,7 @@ function buildLinkMappings({ config, configPath, exists, target }) {
|
|
|
241
321
|
return { mappings, error: false };
|
|
242
322
|
}
|
|
243
323
|
|
|
244
|
-
function linkSkills() {
|
|
324
|
+
function linkSkills(rootDir, options = {}) {
|
|
245
325
|
const { config, configPath, exists, error } = readSkillsConfig(rootDir);
|
|
246
326
|
if (error) {
|
|
247
327
|
console.error(
|
|
@@ -250,7 +330,7 @@ function linkSkills() {
|
|
|
250
330
|
return 1;
|
|
251
331
|
}
|
|
252
332
|
|
|
253
|
-
const target = resolveTarget(config);
|
|
333
|
+
const target = resolveTarget(config, rootDir);
|
|
254
334
|
if (!ensureTarget(target)) {
|
|
255
335
|
return 1;
|
|
256
336
|
}
|
|
@@ -260,6 +340,9 @@ function linkSkills() {
|
|
|
260
340
|
configPath,
|
|
261
341
|
exists,
|
|
262
342
|
target,
|
|
343
|
+
rootDir,
|
|
344
|
+
linkRoot: options.linkRoot,
|
|
345
|
+
explicitTools: options.explicitTools,
|
|
263
346
|
});
|
|
264
347
|
if (mappingError) {
|
|
265
348
|
return 1;
|
|
@@ -272,7 +355,6 @@ function linkSkills() {
|
|
|
272
355
|
return 0;
|
|
273
356
|
}
|
|
274
357
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
358
|
+
module.exports = {
|
|
359
|
+
linkSkills,
|
|
360
|
+
};
|