skill-tags 1.2.1 → 1.3.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/README.md +86 -18
- package/bin/categories.js +6 -44
- package/bin/postinstall.js +11 -5
- package/bin/setup.js +218 -0
- package/bin/skill-tags.js +16 -78
- package/bin/st-skills.js +36 -0
- package/package.json +3 -2
- package/sync.sh +8 -8
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ If you're interested in contributing to Cursor Kits, please let me know!
|
|
|
16
16
|
- [Agent Setup Prompt](#agent-setup-prompt)
|
|
17
17
|
- [Install Options](#install-options)
|
|
18
18
|
- [How It Works](#how-it-works)
|
|
19
|
+
- [Project-Level Install](#project-level-install)
|
|
19
20
|
- [CLI Reference](#cli-reference)
|
|
20
21
|
- [Manual Sync](#manual-sync)
|
|
21
22
|
- [Skill Sources Scanned](#skill-sources-scanned)
|
|
@@ -29,23 +30,38 @@ If you're interested in contributing to Cursor Kits, please let me know!
|
|
|
29
30
|
|
|
30
31
|
## Quick Start
|
|
31
32
|
|
|
33
|
+
### Global (recommended for most users)
|
|
34
|
+
|
|
35
|
+
1. **Install:** `npm install skill-tags -g`
|
|
36
|
+
2. **Setup:** `skill-tags --setup` → choose **Global** → `source ~/.zshrc`
|
|
37
|
+
3. **Sync:** `skill-tags` (generates the command file)
|
|
38
|
+
4. **Use:** Reference `@skill-tags.md` in any Cursor chat
|
|
39
|
+
|
|
32
40
|
```bash
|
|
33
|
-
# Install
|
|
34
41
|
npm install skill-tags -g
|
|
35
|
-
|
|
36
|
-
# Add the shell auto-trigger wrapper
|
|
37
|
-
skill-tags --setup
|
|
42
|
+
skill-tags --setup # choose: Global
|
|
38
43
|
source ~/.zshrc
|
|
39
|
-
|
|
40
|
-
# Initial sync (generate command file)
|
|
41
44
|
skill-tags
|
|
42
45
|
```
|
|
43
46
|
|
|
47
|
+
### Project-level
|
|
48
|
+
|
|
49
|
+
1. **Install:** `npm install skill-tags --save-dev`
|
|
50
|
+
2. **Setup:** `npx skill-tags --setup` → choose **Project**
|
|
51
|
+
3. **Use:** Reference `@project-skill-tags.md` in Cursor chat
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install skill-tags --save-dev
|
|
55
|
+
npx skill-tags --setup # choose: Project — adds "skills" script to package.json
|
|
56
|
+
|
|
57
|
+
npm run skills add owner/repo/skill-name # adds skill + auto-syncs
|
|
58
|
+
```
|
|
59
|
+
|
|
44
60
|
## Usage
|
|
45
61
|
|
|
46
62
|
In any Cursor chat, attach the full index of your skills by referencing the generated command file:
|
|
47
63
|
|
|
48
|
-
- `@skill-tags.md`
|
|
64
|
+
- `@skill-tags.md` (global) or `@project-skill-tags.md` (local, from `skill-tags --local`)
|
|
49
65
|
|
|
50
66
|
Example:
|
|
51
67
|
|
|
@@ -94,9 +110,9 @@ Use a terminal that runs outside the sandbox with full permissions to avoid perm
|
|
|
94
110
|
|
|
95
111
|
Steps to perform:
|
|
96
112
|
1. Install the package: `npm install skill-tags -g`
|
|
97
|
-
2. Run `skill-tags --setup`
|
|
98
|
-
3.
|
|
99
|
-
4.
|
|
113
|
+
2. Run `skill-tags --setup` — when prompted, choose Global and Auto (recommended)
|
|
114
|
+
3. Source my shell rc file or instruct me to do so manually
|
|
115
|
+
4. Run `skill-tags` to perform an initial sync of all installed skills
|
|
100
116
|
|
|
101
117
|
When complete, output a summary that includes:
|
|
102
118
|
- Where the command file was generated (~/.cursor/commands/skill-tags.md)
|
|
@@ -113,14 +129,23 @@ When complete, output a summary that includes:
|
|
|
113
129
|
|
|
114
130
|
## Install Options
|
|
115
131
|
|
|
116
|
-
###
|
|
132
|
+
### Global install (npm)
|
|
117
133
|
|
|
118
134
|
```bash
|
|
119
135
|
npm install skill-tags -g
|
|
120
|
-
skill-tags --setup
|
|
136
|
+
skill-tags --setup # choose: Global
|
|
121
137
|
source ~/.zshrc
|
|
122
138
|
```
|
|
123
139
|
|
|
140
|
+
### Project install (npm)
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm install skill-tags --save-dev
|
|
144
|
+
npx skill-tags --setup # choose: Project
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Adds `"skills": "st-skills"` to `package.json`. Use `npm run skills add <pkg>` to add project skills — auto-syncs `.cursor/commands/project-skill-tags.md` on every change.
|
|
148
|
+
|
|
124
149
|
### One-off run (no install)
|
|
125
150
|
|
|
126
151
|
```bash
|
|
@@ -141,31 +166,74 @@ source ~/.zshrc # or ~/.bash_profile / ~/.bashrc
|
|
|
141
166
|
|
|
142
167
|
## How It Works
|
|
143
168
|
|
|
144
|
-
After setup, the `skills` command wraps `npx skills` and automatically runs a sync after every `skills add` or `skills remove`:
|
|
169
|
+
After global setup, the `skills` command wraps `npx skills` and automatically runs a sync after every `skills add` or `skills remove`:
|
|
145
170
|
|
|
146
171
|
```bash
|
|
147
172
|
# Install (or remove) a skill — sync runs automatically
|
|
148
173
|
skills add vercel-labs/agent-skills/vercel-react-best-practices
|
|
149
174
|
|
|
150
|
-
# The
|
|
175
|
+
# The command file is updated:
|
|
151
176
|
# ~/.cursor/commands/skill-tags.md
|
|
152
177
|
|
|
153
178
|
# Use it in Cursor chat:
|
|
154
179
|
# @skill-tags.md
|
|
155
180
|
```
|
|
156
181
|
|
|
182
|
+
## Project-Level Install
|
|
183
|
+
|
|
184
|
+
Install skill-tags as a dev dependency to manage project-specific skills without touching your global shell config. The `--setup` wizard adds a `"skills"` npm script backed by the bundled `st-skills` binary.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npm install skill-tags --save-dev
|
|
188
|
+
npx skill-tags --setup # choose: Project
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
After setup, `package.json` will include:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
"scripts": {
|
|
195
|
+
"skills": "st-skills"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Use `npm run skills` to add, remove, or update project skills:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npm run skills add owner/repo/skill-name # adds skill + auto-syncs
|
|
203
|
+
npm run skills remove owner/repo/skill-name # removes skill + auto-syncs
|
|
204
|
+
npm run skills update owner/repo/skill-name # updates skill + auto-syncs
|
|
205
|
+
|
|
206
|
+
# Manual re-sync:
|
|
207
|
+
skill-tags --local
|
|
208
|
+
|
|
209
|
+
# Use in Cursor chat:
|
|
210
|
+
# @project-skill-tags.md
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The `st-skills` binary (from this package) wraps `npx skills`, then automatically runs `skill-tags --local` on every successful `add`, `remove`, or `update`, writing the index to `.cursor/commands/project-skill-tags.md`.
|
|
214
|
+
|
|
157
215
|
## CLI Reference
|
|
158
216
|
|
|
159
217
|
```bash
|
|
160
218
|
skill-tags # sync all skills, generate/update the command file
|
|
161
219
|
skill-tags --categories # open interactive category wizard (CRUD)
|
|
162
|
-
skill-tags --setup #
|
|
163
|
-
skill-tags --global
|
|
164
|
-
skill-tags --
|
|
220
|
+
skill-tags --setup # interactive setup: choose Global (shell profile) or Project (package.json)
|
|
221
|
+
skill-tags --global # skip local skills (.agents/skills in CWD); scan global sources only
|
|
222
|
+
skill-tags --local # scan only .agents/skills in CWD; write to .cursor/commands/project-skill-tags.md
|
|
165
223
|
skill-tags --version # print version
|
|
166
224
|
skill-tags --help # show usage
|
|
167
225
|
```
|
|
168
226
|
|
|
227
|
+
### `st-skills` (project binary)
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
npm run skills add <pkg> # install a project skill + auto-sync
|
|
231
|
+
npm run skills remove <pkg> # remove a project skill + auto-sync
|
|
232
|
+
npm run skills update <pkg> # update a project skill + auto-sync
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`st-skills` is registered in `package.json` by `skill-tags --setup` (local mode). It wraps `npx skills` and calls `skill-tags --local` after every mutating command.
|
|
236
|
+
|
|
169
237
|
## Manual Sync
|
|
170
238
|
|
|
171
239
|
Run at any time to regenerate the command file:
|
|
@@ -192,7 +260,7 @@ Skills are discovered from all of these locations automatically. When the same s
|
|
|
192
260
|
|
|
193
261
|
## Generated File Format
|
|
194
262
|
|
|
195
|
-
The `~/.cursor/commands/skill-tags.md`
|
|
263
|
+
The `~/.cursor/commands/skill-tags.md` (global) and `./.cursor/commands/project-skill-tags.md` (local) contain:
|
|
196
264
|
|
|
197
265
|
- An opening instruction for the agent to assess all skills and apply them autonomously.
|
|
198
266
|
- Instructions for the agent on how to plan with skills when in Plan Mode.
|
package/bin/categories.js
CHANGED
|
@@ -212,11 +212,6 @@ function toTitleCase(str) {
|
|
|
212
212
|
return str.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
function truncate(str, len) {
|
|
216
|
-
if (!str || str.length <= len) return str || '';
|
|
217
|
-
return str.slice(0, len - 1) + '…';
|
|
218
|
-
}
|
|
219
|
-
|
|
220
215
|
// ─── Wizard actions ──────────────────────────────────────────────────────────
|
|
221
216
|
|
|
222
217
|
async function addCategories(skills, config) {
|
|
@@ -256,25 +251,9 @@ async function addCategories(skills, config) {
|
|
|
256
251
|
}
|
|
257
252
|
|
|
258
253
|
for (const cat of selected) {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
name: `${s.name}${hint}`,
|
|
264
|
-
value: s.name,
|
|
265
|
-
checked: !!match,
|
|
266
|
-
description: truncate(s.description, 60),
|
|
267
|
-
};
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
const selectedSkills = await checkbox({
|
|
271
|
-
message: `Select skills for "${cat}" (space to toggle)`,
|
|
272
|
-
choices: skillChoices,
|
|
273
|
-
pageSize: 15,
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
config[cat] = selectedSkills;
|
|
277
|
-
console.log(` ✓ ${toTitleCase(cat)}: ${selectedSkills.length} skill(s)\n`);
|
|
254
|
+
const matched = skills.filter(s => matchKeywords(s, cat));
|
|
255
|
+
config[cat] = matched.map(s => s.name);
|
|
256
|
+
console.log(` ✓ ${toTitleCase(cat)}: ${matched.length} skill(s) auto-assigned\n`);
|
|
278
257
|
}
|
|
279
258
|
}
|
|
280
259
|
|
|
@@ -293,26 +272,9 @@ async function editCategory(skills, config) {
|
|
|
293
272
|
})),
|
|
294
273
|
});
|
|
295
274
|
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const hint = match ? ` — ${match.reason}` : '';
|
|
300
|
-
return {
|
|
301
|
-
name: `${s.name}${hint}`,
|
|
302
|
-
value: s.name,
|
|
303
|
-
checked: currentSet.has(s.name),
|
|
304
|
-
description: truncate(s.description, 60),
|
|
305
|
-
};
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
const selectedSkills = await checkbox({
|
|
309
|
-
message: `Select skills for "${cat}" (space to toggle)`,
|
|
310
|
-
choices: skillChoices,
|
|
311
|
-
pageSize: 15,
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
config[cat] = selectedSkills;
|
|
315
|
-
console.log(` ✓ Updated ${toTitleCase(cat)}: ${selectedSkills.length} skill(s)\n`);
|
|
275
|
+
const matched = skills.filter(s => matchKeywords(s, cat));
|
|
276
|
+
config[cat] = matched.map(s => s.name);
|
|
277
|
+
console.log(` ✓ Re-generated ${toTitleCase(cat)}: ${matched.length} skill(s) auto-assigned\n`);
|
|
316
278
|
}
|
|
317
279
|
|
|
318
280
|
async function deleteCategory(config) {
|
package/bin/postinstall.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// bin/postinstall.js
|
|
3
|
-
// Runs automatically after `npm install
|
|
4
|
-
//
|
|
3
|
+
// Runs automatically after `npm install skill-tags`.
|
|
4
|
+
// Global installs: perform initial sync. Local installs: print setup guidance.
|
|
5
5
|
// Never throws — a failed postinstall should never break npm install.
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
@@ -15,12 +15,18 @@ if (process.platform === 'win32') {
|
|
|
15
15
|
process.exit(0);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (
|
|
18
|
+
const isGlobal = process.env.npm_config_global === 'true';
|
|
19
|
+
|
|
20
|
+
if (!isGlobal) {
|
|
21
|
+
// Project (local) install — print setup guidance and exit cleanly
|
|
22
|
+
console.log('\n skill-tags installed as a project dependency.');
|
|
23
|
+
console.log(' Run setup to add the "skills" npm script to your package.json:\n');
|
|
24
|
+
console.log(' npx skill-tags --setup\n');
|
|
21
25
|
process.exit(0);
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
// ─── Global install: run initial sync ────────────────────────────────────────
|
|
29
|
+
|
|
24
30
|
const syncScript = path.join(__dirname, '..', 'sync.sh');
|
|
25
31
|
|
|
26
32
|
if (!fs.existsSync(syncScript)) {
|
package/bin/setup.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/setup.js
|
|
3
|
+
// Interactive setup wizard: global/project install mode + auto/manual sync preference.
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const { spawnSync } = require('child_process');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
|
|
12
|
+
const HOME = os.homedir();
|
|
13
|
+
const SYNC_SCRIPT = path.join(__dirname, '..', 'sync.sh');
|
|
14
|
+
const WRAPPER_MARKER = '# ─── skill-tags / Cursor Skill Command Sync';
|
|
15
|
+
|
|
16
|
+
module.exports = async function runSetup() {
|
|
17
|
+
const { select, confirm } = require('@inquirer/prompts');
|
|
18
|
+
|
|
19
|
+
console.log('\n skill-tags: setup\n');
|
|
20
|
+
|
|
21
|
+
const isGlobal = process.env.npm_config_global === 'true';
|
|
22
|
+
|
|
23
|
+
const mode = await select({
|
|
24
|
+
message: 'How are you using skill-tags?',
|
|
25
|
+
choices: [
|
|
26
|
+
{
|
|
27
|
+
name: 'Global — add skills() wrapper to shell profile (recommended for most users)',
|
|
28
|
+
value: 'global',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'Project — add "skills" npm script to this project\'s package.json',
|
|
32
|
+
value: 'project',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
default: isGlobal ? 'global' : 'project',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const syncMode = await select({
|
|
39
|
+
message: 'How should skill-tags sync?',
|
|
40
|
+
choices: [
|
|
41
|
+
{
|
|
42
|
+
name: 'Auto (recommended) — sync automatically whenever you add or remove a skill',
|
|
43
|
+
value: 'auto',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'Manual — run skill-tags yourself to sync when needed',
|
|
47
|
+
value: 'manual',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
default: 'auto',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (mode === 'global') {
|
|
54
|
+
await setupGlobal(confirm, syncMode);
|
|
55
|
+
} else {
|
|
56
|
+
await setupProject(confirm, syncMode);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// ─── Global setup ─────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
async function setupGlobal(confirm, syncMode) {
|
|
63
|
+
const shellName = path.basename(process.env.SHELL || 'bash');
|
|
64
|
+
let rcFile;
|
|
65
|
+
if (shellName === 'zsh') rcFile = path.join(HOME, '.zshrc');
|
|
66
|
+
else if (process.platform === 'darwin') rcFile = path.join(HOME, '.bash_profile');
|
|
67
|
+
else rcFile = path.join(HOME, '.bashrc');
|
|
68
|
+
|
|
69
|
+
const displayRc = rcFile.replace(HOME, '~');
|
|
70
|
+
|
|
71
|
+
if (syncMode === 'manual') {
|
|
72
|
+
console.log('\n Manual sync selected.');
|
|
73
|
+
console.log(' Run skill-tags at any time to regenerate ~/.cursor/commands/skill-tags.md\n');
|
|
74
|
+
console.log(' Running initial sync...\n');
|
|
75
|
+
spawnSync('bash', [SYNC_SCRIPT], { stdio: 'inherit' });
|
|
76
|
+
console.log(`\n Done! Run skill-tags manually to re-sync.\n`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Auto sync: add shell wrapper
|
|
81
|
+
let alreadyInstalled = false;
|
|
82
|
+
try {
|
|
83
|
+
const content = fs.readFileSync(rcFile, 'utf-8');
|
|
84
|
+
if (content.includes(WRAPPER_MARKER)) alreadyInstalled = true;
|
|
85
|
+
} catch {}
|
|
86
|
+
|
|
87
|
+
if (alreadyInstalled) {
|
|
88
|
+
console.log(`\n ✓ Shell wrapper already installed in ${displayRc}\n`);
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(`\n This will add a skills() shell wrapper to ${displayRc}`);
|
|
93
|
+
console.log(` It auto-syncs skill-tags.md after every skills add/remove.\n`);
|
|
94
|
+
|
|
95
|
+
const yes = await confirm({
|
|
96
|
+
message: `Add the wrapper to ${displayRc}?`,
|
|
97
|
+
default: true,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!yes) {
|
|
101
|
+
console.log('\n Skipped.\n');
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const syncPath = fs.existsSync(path.join(HOME, '.cursor', 'sync-skill-commands.sh'))
|
|
106
|
+
? path.join(HOME, '.cursor', 'sync-skill-commands.sh')
|
|
107
|
+
: SYNC_SCRIPT;
|
|
108
|
+
|
|
109
|
+
const wrapper = `
|
|
110
|
+
${WRAPPER_MARKER} ────────────────────────────────────────────────
|
|
111
|
+
# Wraps \`npx skills\` to auto-generate skill-tags.md after install/removal.
|
|
112
|
+
# Run manually: skill-tags (or: bash ${syncPath})
|
|
113
|
+
function skills() {
|
|
114
|
+
npx skills "$@"
|
|
115
|
+
local exit_code=$?
|
|
116
|
+
if [[ "$1" == "add" || "$1" == "remove" ]] && [[ $exit_code -eq 0 ]]; then
|
|
117
|
+
bash "${syncPath}"
|
|
118
|
+
fi
|
|
119
|
+
return $exit_code
|
|
120
|
+
}
|
|
121
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
122
|
+
`;
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
fs.appendFileSync(rcFile, wrapper);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.error(`\n Failed to write to ${displayRc}: ${err.message}\n`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(`\n ✓ Added skills() wrapper to ${displayRc}`);
|
|
132
|
+
console.log(` Reload with: source ${displayRc}\n`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ─── Project setup ────────────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
async function setupProject(confirm, syncMode) {
|
|
138
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
139
|
+
|
|
140
|
+
if (!fs.existsSync(pkgPath)) {
|
|
141
|
+
console.error('\n No package.json found in current directory.');
|
|
142
|
+
console.error(' Run skill-tags --setup from your project root.\n');
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let pkg;
|
|
147
|
+
try {
|
|
148
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error(`\n Failed to read package.json: ${err.message}\n`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const scripts = pkg.scripts || {};
|
|
155
|
+
|
|
156
|
+
if (syncMode === 'manual') {
|
|
157
|
+
console.log('\n Manual sync selected.');
|
|
158
|
+
console.log(' Run skill-tags --local at any time to regenerate .cursor/commands/project-skill-tags.md\n');
|
|
159
|
+
console.log(' Running initial project sync...\n');
|
|
160
|
+
spawnSync(
|
|
161
|
+
'node',
|
|
162
|
+
[path.join(__dirname, 'skill-tags.js'), '--local'],
|
|
163
|
+
{ stdio: 'inherit', cwd: process.cwd() }
|
|
164
|
+
);
|
|
165
|
+
console.log(`\n Done! Run skill-tags --local manually to re-sync.\n`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Auto sync: add "skills" npm script
|
|
170
|
+
if (scripts.skills === 'st-skills') {
|
|
171
|
+
console.log('\n ✓ Already configured — "skills": "st-skills" is in package.json\n');
|
|
172
|
+
console.log(' Add a skill: npm run skills add <owner/repo/skill-name>');
|
|
173
|
+
console.log(' Remove: npm run skills remove <owner/repo/skill-name>\n');
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log('\n This will add a "skills" script to your package.json:');
|
|
178
|
+
console.log(' "scripts": { "skills": "st-skills" }');
|
|
179
|
+
console.log('\n Then use: npm run skills add <owner/repo/skill-name>');
|
|
180
|
+
console.log(' Auto-syncs .cursor/commands/project-skill-tags.md after every add/remove.\n');
|
|
181
|
+
|
|
182
|
+
const yes = await confirm({
|
|
183
|
+
message: 'Add the "skills" script to package.json?',
|
|
184
|
+
default: true,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (!yes) {
|
|
188
|
+
console.log('\n Skipped.\n');
|
|
189
|
+
process.exit(0);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
pkg.scripts = { ...scripts, skills: 'st-skills' };
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.error(`\n Failed to write package.json: ${err.message}\n`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log('\n ✓ Added "skills": "st-skills" to package.json');
|
|
202
|
+
console.log('\n Running initial project sync...\n');
|
|
203
|
+
|
|
204
|
+
const result = spawnSync(
|
|
205
|
+
'node',
|
|
206
|
+
[path.join(__dirname, 'skill-tags.js'), '--local'],
|
|
207
|
+
{ stdio: 'inherit', cwd: process.cwd() }
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
if (result.error) {
|
|
211
|
+
console.error(`\n Warning: initial sync failed — ${result.error.message}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log('\n Setup complete!\n');
|
|
215
|
+
console.log(' Add a skill: npm run skills add <owner/repo/skill-name>');
|
|
216
|
+
console.log(' Remove: npm run skills remove <owner/repo/skill-name>');
|
|
217
|
+
console.log(' Manual sync: skill-tags --local\n');
|
|
218
|
+
}
|
package/bin/skill-tags.js
CHANGED
|
@@ -7,12 +7,9 @@
|
|
|
7
7
|
const { spawnSync } = require('child_process');
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const fs = require('fs');
|
|
10
|
-
const os = require('os');
|
|
11
10
|
|
|
12
11
|
const VERSION = require('../package.json').version;
|
|
13
|
-
const HOME = os.homedir();
|
|
14
12
|
const SYNC_SCRIPT = path.join(__dirname, '..', 'sync.sh');
|
|
15
|
-
const WRAPPER_MARKER = '# ─── skill-tags / Cursor Skill Command Sync';
|
|
16
13
|
|
|
17
14
|
// ─── --version ───────────────────────────────────────────────────────────────
|
|
18
15
|
|
|
@@ -40,9 +37,9 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
|
40
37
|
Options:
|
|
41
38
|
(none) Scan all skill sources and generate skill-tags.md
|
|
42
39
|
--categories Interactive category wizard (arrow keys + space to toggle)
|
|
43
|
-
--global
|
|
44
|
-
--
|
|
45
|
-
--setup
|
|
40
|
+
--global Skip local skills (.agents/skills in CWD); scan global sources only
|
|
41
|
+
--local Scan only .agents/skills in CWD; write to .cursor/commands/project-skill-tags.md
|
|
42
|
+
--setup Interactive setup: choose Global (shell profile) or Project (package.json)
|
|
46
43
|
--version, -v Print version
|
|
47
44
|
--help, -h Show this help
|
|
48
45
|
|
|
@@ -55,12 +52,21 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
|
55
52
|
./.agents/skills/ project-level skills (current directory)
|
|
56
53
|
|
|
57
54
|
Output:
|
|
58
|
-
~/.cursor/commands/skill-tags.md
|
|
59
|
-
~/.cursor/commands/skills-<category>.md
|
|
60
|
-
./.cursor/commands/skill-tags.md
|
|
55
|
+
~/.cursor/commands/skill-tags.md full index of all skills (global)
|
|
56
|
+
~/.cursor/commands/skills-<category>.md generated by --categories
|
|
57
|
+
./.cursor/commands/project-skill-tags.md generated by --local
|
|
61
58
|
|
|
62
59
|
Config:
|
|
63
60
|
~/.cursor/skill-tags-categories.conf
|
|
61
|
+
|
|
62
|
+
Setup modes:
|
|
63
|
+
Global Adds skills() wrapper to ~/.zshrc — use: skills add <pkg>
|
|
64
|
+
Project Adds "skills": "st-skills" to package.json — use: npm run skills add <pkg>
|
|
65
|
+
|
|
66
|
+
Project binary (st-skills):
|
|
67
|
+
st-skills add <pkg> Install a project skill and auto-sync
|
|
68
|
+
st-skills remove <pkg> Remove a project skill and auto-sync
|
|
69
|
+
st-skills update <pkg> Update a project skill and auto-sync
|
|
64
70
|
`);
|
|
65
71
|
process.exit(0);
|
|
66
72
|
}
|
|
@@ -74,6 +80,7 @@ if (process.argv.includes('--categories')) {
|
|
|
74
80
|
// ─── --setup ─────────────────────────────────────────────────────────────────
|
|
75
81
|
|
|
76
82
|
else if (process.argv.includes('--setup')) {
|
|
83
|
+
const runSetup = require('./setup');
|
|
77
84
|
runSetup().catch(err => {
|
|
78
85
|
if (err.name === 'ExitPromptError') { console.log(); process.exit(0); }
|
|
79
86
|
console.error(' Error:', err.message);
|
|
@@ -100,72 +107,3 @@ else {
|
|
|
100
107
|
|
|
101
108
|
process.exit(result.status ?? 0);
|
|
102
109
|
}
|
|
103
|
-
|
|
104
|
-
// ─── Setup implementation ────────────────────────────────────────────────────
|
|
105
|
-
|
|
106
|
-
async function runSetup() {
|
|
107
|
-
const { confirm } = require('@inquirer/prompts');
|
|
108
|
-
|
|
109
|
-
const shellName = path.basename(process.env.SHELL || 'bash');
|
|
110
|
-
let rcFile;
|
|
111
|
-
if (shellName === 'zsh') rcFile = path.join(HOME, '.zshrc');
|
|
112
|
-
else if (process.platform === 'darwin') rcFile = path.join(HOME, '.bash_profile');
|
|
113
|
-
else rcFile = path.join(HOME, '.bashrc');
|
|
114
|
-
|
|
115
|
-
const displayRc = rcFile.replace(HOME, '~');
|
|
116
|
-
|
|
117
|
-
console.log(`\n skill-tags: shell setup\n`);
|
|
118
|
-
|
|
119
|
-
let alreadyInstalled = false;
|
|
120
|
-
try {
|
|
121
|
-
const content = fs.readFileSync(rcFile, 'utf-8');
|
|
122
|
-
if (content.includes(WRAPPER_MARKER)) alreadyInstalled = true;
|
|
123
|
-
} catch {}
|
|
124
|
-
|
|
125
|
-
if (alreadyInstalled) {
|
|
126
|
-
console.log(` ✓ Shell wrapper already installed in ${displayRc}\n`);
|
|
127
|
-
process.exit(0);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
console.log(` This will add a skills() shell wrapper to ${displayRc}`);
|
|
131
|
-
console.log(` It auto-syncs skill-tags.md after every skills add/remove.\n`);
|
|
132
|
-
|
|
133
|
-
const yes = await confirm({
|
|
134
|
-
message: `Add the wrapper to ${displayRc}?`,
|
|
135
|
-
default: true,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
if (!yes) {
|
|
139
|
-
console.log('\n Skipped.\n');
|
|
140
|
-
process.exit(0);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const syncPath = fs.existsSync(path.join(HOME, '.cursor', 'sync-skill-commands.sh'))
|
|
144
|
-
? path.join(HOME, '.cursor', 'sync-skill-commands.sh')
|
|
145
|
-
: SYNC_SCRIPT;
|
|
146
|
-
|
|
147
|
-
const wrapper = `
|
|
148
|
-
${WRAPPER_MARKER} ────────────────────────────────────────────────
|
|
149
|
-
# Wraps \`npx skills\` to auto-generate skill-tags.md after install/removal.
|
|
150
|
-
# Run manually: skill-tags (or: bash ${syncPath})
|
|
151
|
-
function skills() {
|
|
152
|
-
npx skills "$@"
|
|
153
|
-
local exit_code=$?
|
|
154
|
-
if [[ "$1" == "add" || "$1" == "remove" ]] && [[ $exit_code -eq 0 ]]; then
|
|
155
|
-
bash "${syncPath}"
|
|
156
|
-
fi
|
|
157
|
-
return $exit_code
|
|
158
|
-
}
|
|
159
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
160
|
-
`;
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
fs.appendFileSync(rcFile, wrapper);
|
|
164
|
-
} catch (err) {
|
|
165
|
-
console.error(`\n Failed to write to ${displayRc}: ${err.message}\n`);
|
|
166
|
-
process.exit(1);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
console.log(`\n ✓ Added skills() wrapper to ${displayRc}`);
|
|
170
|
-
console.log(` Reload with: source ${displayRc}\n`);
|
|
171
|
-
}
|
package/bin/st-skills.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/st-skills.js
|
|
3
|
+
// Project-level skills wrapper: runs npx skills and auto-syncs project-skill-tags.md on add/remove/update.
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const { spawnSync } = require('child_process');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
const subcommand = args[0];
|
|
12
|
+
|
|
13
|
+
// Run npx skills with all forwarded arguments
|
|
14
|
+
const result = spawnSync('npx', ['skills', ...args], { stdio: 'inherit', shell: false });
|
|
15
|
+
|
|
16
|
+
if (result.error) {
|
|
17
|
+
console.error(`\n st-skills: failed to run npx skills — ${result.error.message}\n`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const exitCode = result.status ?? 1;
|
|
22
|
+
|
|
23
|
+
// Auto-sync project-skill-tags.md on successful mutating subcommands
|
|
24
|
+
if (['add', 'remove', 'update'].includes(subcommand) && exitCode === 0) {
|
|
25
|
+
const syncResult = spawnSync(
|
|
26
|
+
'node',
|
|
27
|
+
[path.join(__dirname, 'skill-tags.js'), '--local'],
|
|
28
|
+
{ stdio: 'inherit', cwd: process.cwd() }
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (syncResult.error) {
|
|
32
|
+
console.error(`\n st-skills: sync failed — ${syncResult.error.message}\n`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
process.exit(exitCode);
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skill-tags",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Generates consolidated command files (@skill-tags.md and custom @skills-<category>.md indexes) for all installed Cursor skills.",
|
|
5
5
|
"bin": {
|
|
6
|
-
"skill-tags": "bin/skill-tags.js"
|
|
6
|
+
"skill-tags": "bin/skill-tags.js",
|
|
7
|
+
"st-skills": "bin/st-skills.js"
|
|
7
8
|
},
|
|
8
9
|
"scripts": {
|
|
9
10
|
"postinstall": "node ./bin/postinstall.js"
|
package/sync.sh
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
|
|
10
|
-
VERSION="1.
|
|
10
|
+
VERSION="1.3.0"
|
|
11
11
|
|
|
12
12
|
GLOBAL_COMMANDS_DIR="${HOME}/.cursor/commands"
|
|
13
13
|
OUTPUT_FILE="${GLOBAL_COMMANDS_DIR}/skill-tags.md"
|
|
14
14
|
CATEGORIES_CONFIG="${HOME}/.cursor/skill-tags-categories.conf"
|
|
15
15
|
PROJECT_COMMANDS_DIR="$(pwd)/.cursor/commands"
|
|
16
|
-
PROJECT_OUTPUT_FILE="${PROJECT_COMMANDS_DIR}/skill-tags.md"
|
|
16
|
+
PROJECT_OUTPUT_FILE="${PROJECT_COMMANDS_DIR}/project-skill-tags.md"
|
|
17
17
|
|
|
18
18
|
# ─── Priority-ordered skill source directories ─────────────────────────────────
|
|
19
19
|
# Earlier entries take priority when the same skill name exists in multiple locations.
|
|
@@ -39,8 +39,8 @@ PROJECT_ONLY=false
|
|
|
39
39
|
|
|
40
40
|
for arg in "$@"; do
|
|
41
41
|
case "$arg" in
|
|
42
|
-
--global
|
|
43
|
-
--
|
|
42
|
+
--global) GLOBAL_ONLY=true ;;
|
|
43
|
+
--local) PROJECT_ONLY=true ;;
|
|
44
44
|
esac
|
|
45
45
|
done
|
|
46
46
|
|
|
@@ -259,7 +259,7 @@ EOF
|
|
|
259
259
|
printf "\n skill-tags v%s — syncing skills\n\n" "$VERSION"
|
|
260
260
|
|
|
261
261
|
if [[ "$PROJECT_ONLY" == "true" ]]; then
|
|
262
|
-
# ─── --
|
|
262
|
+
# ─── --local: scan only .agents/skills in CWD ───────────────────────────────
|
|
263
263
|
PROJECT_SKILLS_DIR="$(pwd)/.agents/skills"
|
|
264
264
|
|
|
265
265
|
if [[ ! -d "$PROJECT_SKILLS_DIR" ]]; then
|
|
@@ -299,12 +299,12 @@ EOF
|
|
|
299
299
|
|
|
300
300
|
printf "\n"
|
|
301
301
|
if [[ "$is_update" == "true" ]]; then
|
|
302
|
-
printf " ✓ Updated: .cursor/commands/skill-tags.md\n"
|
|
302
|
+
printf " ✓ Updated: .cursor/commands/project-skill-tags.md\n"
|
|
303
303
|
else
|
|
304
|
-
printf " ✓ Generated: .cursor/commands/skill-tags.md\n"
|
|
304
|
+
printf " ✓ Generated: .cursor/commands/project-skill-tags.md\n"
|
|
305
305
|
fi
|
|
306
306
|
printf " Skills: %d indexed\n" "$count_found"
|
|
307
|
-
printf "\n Tip: type @skill-tags.md in Cursor chat to load the project skills reference.\n\n"
|
|
307
|
+
printf "\n Tip: type @project-skill-tags.md in Cursor chat to load the project skills reference.\n\n"
|
|
308
308
|
|
|
309
309
|
else
|
|
310
310
|
# ─── Default: scan all sources ───────────────────────────────────────────────
|