skill-tags 1.2.0 → 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 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
 
32
- ```bash
33
- # Install (global, recommended)
34
- npm install -g skill-tags
33
+ ### Global (recommended for most users)
35
34
 
36
- # Add the shell auto-trigger wrapper
37
- skill-tags --setup
38
- source ~/.zshrc
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
39
 
40
- # Initial sync (generate command file)
40
+ ```bash
41
+ npm install skill-tags -g
42
+ skill-tags --setup # choose: Global
43
+ source ~/.zshrc
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
 
@@ -90,17 +106,13 @@ Copy and paste this into your Cursor agent to autoconfigure skill-tags:
90
106
  ```text
91
107
  Install and configure the `skill-tags` package (npm: https://www.npmjs.com/package/skill-tags, github: https://github.com/steve-piece/skill-tags) for me.
92
108
 
93
- First, confirm with me: should this be a global install (adds `skill-tags` to PATH, recommended for most users) or a local project devDependency? Wait for my answer before proceeding.
94
-
95
- Once confirmed, use a terminal that runs outside the sandbox with full permissions to avoid permission errors during install. In Cursor, this means using a non-sandboxed terminal session if available (required_permissions: ["all"] if running via agent shell tools).
109
+ Use a terminal that runs outside the sandbox with full permissions to avoid permission errors during install. In Cursor, this means using a non-sandboxed terminal session if available (required_permissions: ["all"] if running via agent shell tools).
96
110
 
97
111
  Steps to perform:
98
- 1. Install the package based on my preference:
99
- - Global: `npm install -g skill-tags`
100
- - Local: `npm install --save-dev skill-tags`
101
- 2. Run `skill-tags --setup` to install the `skills()` shell wrapper in my rc file (~/.zshrc or ~/.bash_profile)
102
- 3. Run `skill-tags` to perform an initial sync of all installed skills
103
- 4. Source my shell rc file or instruct me to do so manually
112
+ 1. Install the package: `npm install skill-tags -g`
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
104
116
 
105
117
  When complete, output a summary that includes:
106
118
  - Where the command file was generated (~/.cursor/commands/skill-tags.md)
@@ -108,6 +120,7 @@ When complete, output a summary that includes:
108
120
  - How the auto-trigger works: `skills add/remove <pkg>` now automatically syncs after every install/removal
109
121
  - How to manually re-sync at any time: run `skill-tags`
110
122
  - The total number of skills that were indexed
123
+ - (Optional) To create category-specific indexes for more focused context, run `skill-tags --categories`
111
124
  ```
112
125
 
113
126
  </details>
@@ -116,24 +129,27 @@ When complete, output a summary that includes:
116
129
 
117
130
  ## Install Options
118
131
 
119
- ### Install via npm
132
+ ### Global install (npm)
120
133
 
121
134
  ```bash
122
- # Global install (recommended) — adds `skill-tags` to your PATH
123
- npm install -g skill-tags
135
+ npm install skill-tags -g
136
+ skill-tags --setup # choose: Global
137
+ source ~/.zshrc
138
+ ```
124
139
 
125
- # One-off run without installing
126
- npx skill-tags
140
+ ### Project install (npm)
127
141
 
128
- # Project devDependency (adds to package.json)
129
- npm install --save-dev skill-tags
142
+ ```bash
143
+ npm install skill-tags --save-dev
144
+ npx skill-tags --setup # choose: Project
130
145
  ```
131
146
 
132
- After global install, set up the shell auto-trigger wrapper:
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
+
149
+ ### One-off run (no install)
133
150
 
134
151
  ```bash
135
- skill-tags --setup
136
- source ~/.zshrc
152
+ npx skill-tags
137
153
  ```
138
154
 
139
155
  ### Install via curl
@@ -150,30 +166,74 @@ source ~/.zshrc # or ~/.bash_profile / ~/.bashrc
150
166
 
151
167
  ## How It Works
152
168
 
153
- 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`:
154
170
 
155
171
  ```bash
156
172
  # Install (or remove) a skill — sync runs automatically
157
173
  skills add vercel-labs/agent-skills/vercel-react-best-practices
158
174
 
159
- # The single command file is updated:
175
+ # The command file is updated:
160
176
  # ~/.cursor/commands/skill-tags.md
161
177
 
162
178
  # Use it in Cursor chat:
163
179
  # @skill-tags.md
164
180
  ```
165
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
+
166
215
  ## CLI Reference
167
216
 
168
217
  ```bash
169
- skill-tags # sync all skills, generate/update the command file
170
- skill-tags --categories # open interactive category wizard (CRUD)
171
- skill-tags --setup # install skills() shell wrapper in ~/.zshrc
172
- skill-tags --global-only # skip project-level skills
173
- skill-tags --version # print version
174
- skill-tags --help # show usage
218
+ skill-tags # sync all skills, generate/update the command file
219
+ skill-tags --categories # open interactive category wizard (CRUD)
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
223
+ skill-tags --version # print version
224
+ skill-tags --help # show usage
175
225
  ```
176
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
+
177
237
  ## Manual Sync
178
238
 
179
239
  Run at any time to regenerate the command file:
@@ -200,7 +260,7 @@ Skills are discovered from all of these locations automatically. When the same s
200
260
 
201
261
  ## Generated File Format
202
262
 
203
- The `~/.cursor/commands/skill-tags.md` contains:
263
+ The `~/.cursor/commands/skill-tags.md` (global) and `./.cursor/commands/project-skill-tags.md` (local) contain:
204
264
 
205
265
  - An opening instruction for the agent to assess all skills and apply them autonomously.
206
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 skillChoices = skills.map(s => {
260
- const match = matchKeywords(s, cat);
261
- const hint = match ? ` ${match.reason}` : '';
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 currentSet = new Set(config[cat] || []);
297
- const skillChoices = skills.map(s => {
298
- const match = matchKeywords(s, cat);
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) {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // bin/postinstall.js
3
- // Runs automatically after `npm install -g skill-tags`.
4
- // Performs an initial sync of any already-installed skills.
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
- // Skip on local (non-global) installs to avoid running on every `npm install`
19
- // in a project. npm sets npm_config_global=true for -g installs.
20
- if (process.env.npm_config_global !== 'true') {
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
 
@@ -38,12 +35,13 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
38
35
  Usage: skill-tags [options]
39
36
 
40
37
  Options:
41
- (none) Scan all skill sources and generate skill-tags.md
42
- --categories Interactive category wizard (arrow keys + space to toggle)
43
- --global-only Skip project-level skills (.agents/skills in CWD)
44
- --setup Install the skills() shell wrapper in your shell rc
45
- --version, -v Print version
46
- --help, -h Show this help
38
+ (none) Scan all skill sources and generate skill-tags.md
39
+ --categories Interactive category wizard (arrow keys + space to toggle)
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)
43
+ --version, -v Print version
44
+ --help, -h Show this help
47
45
 
48
46
  Skill sources (priority order):
49
47
  ~/.agents/skills/ skills installed via npx skills add
@@ -54,11 +52,21 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
54
52
  ./.agents/skills/ project-level skills (current directory)
55
53
 
56
54
  Output:
57
- ~/.cursor/commands/skill-tags.md full index of all skills
58
- ~/.cursor/commands/skills-<category>.md generated by --categories
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
59
58
 
60
59
  Config:
61
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
62
70
  `);
63
71
  process.exit(0);
64
72
  }
@@ -72,6 +80,7 @@ if (process.argv.includes('--categories')) {
72
80
  // ─── --setup ─────────────────────────────────────────────────────────────────
73
81
 
74
82
  else if (process.argv.includes('--setup')) {
83
+ const runSetup = require('./setup');
75
84
  runSetup().catch(err => {
76
85
  if (err.name === 'ExitPromptError') { console.log(); process.exit(0); }
77
86
  console.error(' Error:', err.message);
@@ -98,72 +107,3 @@ else {
98
107
 
99
108
  process.exit(result.status ?? 0);
100
109
  }
101
-
102
- // ─── Setup implementation ────────────────────────────────────────────────────
103
-
104
- async function runSetup() {
105
- const { confirm } = require('@inquirer/prompts');
106
-
107
- const shellName = path.basename(process.env.SHELL || 'bash');
108
- let rcFile;
109
- if (shellName === 'zsh') rcFile = path.join(HOME, '.zshrc');
110
- else if (process.platform === 'darwin') rcFile = path.join(HOME, '.bash_profile');
111
- else rcFile = path.join(HOME, '.bashrc');
112
-
113
- const displayRc = rcFile.replace(HOME, '~');
114
-
115
- console.log(`\n skill-tags: shell setup\n`);
116
-
117
- let alreadyInstalled = false;
118
- try {
119
- const content = fs.readFileSync(rcFile, 'utf-8');
120
- if (content.includes(WRAPPER_MARKER)) alreadyInstalled = true;
121
- } catch {}
122
-
123
- if (alreadyInstalled) {
124
- console.log(` ✓ Shell wrapper already installed in ${displayRc}\n`);
125
- process.exit(0);
126
- }
127
-
128
- console.log(` This will add a skills() shell wrapper to ${displayRc}`);
129
- console.log(` It auto-syncs skill-tags.md after every skills add/remove.\n`);
130
-
131
- const yes = await confirm({
132
- message: `Add the wrapper to ${displayRc}?`,
133
- default: true,
134
- });
135
-
136
- if (!yes) {
137
- console.log('\n Skipped.\n');
138
- process.exit(0);
139
- }
140
-
141
- const syncPath = fs.existsSync(path.join(HOME, '.cursor', 'sync-skill-commands.sh'))
142
- ? path.join(HOME, '.cursor', 'sync-skill-commands.sh')
143
- : SYNC_SCRIPT;
144
-
145
- const wrapper = `
146
- ${WRAPPER_MARKER} ────────────────────────────────────────────────
147
- # Wraps \`npx skills\` to auto-generate skill-tags.md after install/removal.
148
- # Run manually: skill-tags (or: bash ${syncPath})
149
- function skills() {
150
- npx skills "$@"
151
- local exit_code=$?
152
- if [[ "$1" == "add" || "$1" == "remove" ]] && [[ $exit_code -eq 0 ]]; then
153
- bash "${syncPath}"
154
- fi
155
- return $exit_code
156
- }
157
- # ─────────────────────────────────────────────────────────────────────────────
158
- `;
159
-
160
- try {
161
- fs.appendFileSync(rcFile, wrapper);
162
- } catch (err) {
163
- console.error(`\n Failed to write to ${displayRc}: ${err.message}\n`);
164
- process.exit(1);
165
- }
166
-
167
- console.log(`\n ✓ Added skills() wrapper to ${displayRc}`);
168
- console.log(` Reload with: source ${displayRc}\n`);
169
- }
@@ -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.2.0",
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,11 +7,13 @@
7
7
 
8
8
  set -euo pipefail
9
9
 
10
- VERSION="1.2.0"
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
+ PROJECT_COMMANDS_DIR="$(pwd)/.cursor/commands"
16
+ PROJECT_OUTPUT_FILE="${PROJECT_COMMANDS_DIR}/project-skill-tags.md"
15
17
 
16
18
  # ─── Priority-ordered skill source directories ─────────────────────────────────
17
19
  # Earlier entries take priority when the same skill name exists in multiple locations.
@@ -33,10 +35,12 @@ trap 'rm -f "$SKILLS_TEMP"; rm -rf "$SKILLS_META_DIR"' EXIT
33
35
  # ─── Flags ─────────────────────────────────────────────────────────────────────
34
36
 
35
37
  GLOBAL_ONLY=false
38
+ PROJECT_ONLY=false
36
39
 
37
40
  for arg in "$@"; do
38
41
  case "$arg" in
39
- --global-only) GLOBAL_ONLY=true ;;
42
+ --global) GLOBAL_ONLY=true ;;
43
+ --local) PROJECT_ONLY=true ;;
40
44
  esac
41
45
  done
42
46
 
@@ -254,27 +258,72 @@ EOF
254
258
 
255
259
  printf "\n skill-tags v%s — syncing skills\n\n" "$VERSION"
256
260
 
257
- # 1. Scan all global/user-level skill sources
258
- for entry in "${GLOBAL_SKILL_SOURCES[@]}"; do
259
- dir="${entry%%:*}"
260
- label="${entry##*:}"
261
- if [[ -d "$dir" ]]; then
262
- log "Scanning ${label}: ${dir/#$HOME/~}"
263
- scan_tree "$dir"
261
+ if [[ "$PROJECT_ONLY" == "true" ]]; then
262
+ # ─── --local: scan only .agents/skills in CWD ───────────────────────────────
263
+ PROJECT_SKILLS_DIR="$(pwd)/.agents/skills"
264
+
265
+ if [[ ! -d "$PROJECT_SKILLS_DIR" ]]; then
266
+ printf " No project skills found at %s/.agents/skills\n\n" "$(pwd)"
267
+ exit 0
264
268
  fi
265
- done
266
269
 
267
- # 2. Project-level skills (.agents/skills in CWD)
268
- if [[ "$GLOBAL_ONLY" == "false" && -d ".agents/skills" ]]; then
269
- log "Scanning project skills: $(pwd)/.agents/skills"
270
- scan_tree "$(pwd)/.agents/skills"
271
- fi
270
+ log "Scanning project skills: ${PROJECT_SKILLS_DIR}"
271
+ scan_tree "$PROJECT_SKILLS_DIR"
272
+
273
+ OPENING="Assess the following project-level skills and apply any that are relevant to completing the user's request at the highest level of efficiency, quality, and completeness. When skills overlap in scope, assess the overlapping skills in greater detail and autonomously determine which is the best match for the project or the specific request — do not prompt the user to resolve overlaps.
274
+
275
+ CRITICAL REQUIREMENT: Before applying any skill, you MUST use the Read tool to read the full contents of the skill file at the provided path. Do not assume the skill's behavior from its title or description alone.
276
+
277
+ If operating in Plan Mode, explicitly include references to specific skills to use and (if applicable) subagents to utilize for efficient programming within the contents of the plan and the plan's TODOs.
278
+
279
+ Examples:
280
+ - \"Use the \`responsive-design/SKILL.md\` to apply advanced clamp-based responsiveness to the new navigation bar.\"
281
+ - \"Delegate to the \`frontend-designer\` subagent using \`ui-ux-pro-max/SKILL.md\` to build the polished component.\"
282
+ - \"Utilize \`supabase-postgres-best-practices/SKILL.md\` when designing the database schema for the user profiles.\""
283
+
284
+ mkdir -p "$PROJECT_COMMANDS_DIR"
285
+
286
+ is_update=false
287
+ [[ -f "$PROJECT_OUTPUT_FILE" ]] && is_update=true
288
+
289
+ cat > "$PROJECT_OUTPUT_FILE" <<EOF
290
+ # Skill Tags Command
291
+
292
+ <!-- Auto-generated by sync.sh (skill-tags) v${VERSION} — do not edit manually -->
293
+
294
+ ${OPENING}
295
+
296
+ ## Available Skills
297
+ $(cat "$SKILLS_TEMP")
298
+ EOF
299
+
300
+ printf "\n"
301
+ if [[ "$is_update" == "true" ]]; then
302
+ printf " ✓ Updated: .cursor/commands/project-skill-tags.md\n"
303
+ else
304
+ printf " ✓ Generated: .cursor/commands/project-skill-tags.md\n"
305
+ fi
306
+ printf " Skills: %d indexed\n" "$count_found"
307
+ printf "\n Tip: type @project-skill-tags.md in Cursor chat to load the project skills reference.\n\n"
272
308
 
273
- # ─── Write skill-tags.md ───────────────────────────────────────────────────────
309
+ else
310
+ # ─── Default: scan all sources ───────────────────────────────────────────────
311
+
312
+ for entry in "${GLOBAL_SKILL_SOURCES[@]}"; do
313
+ dir="${entry%%:*}"
314
+ label="${entry##*:}"
315
+ if [[ -d "$dir" ]]; then
316
+ log "Scanning ${label}: ${dir/#$HOME/~}"
317
+ scan_tree "$dir"
318
+ fi
319
+ done
274
320
 
275
- mkdir -p "$GLOBAL_COMMANDS_DIR"
321
+ if [[ "$GLOBAL_ONLY" == "false" && -d ".agents/skills" ]]; then
322
+ log "Scanning project skills: $(pwd)/.agents/skills"
323
+ scan_tree "$(pwd)/.agents/skills"
324
+ fi
276
325
 
277
- OPENING="Assess the following skills available in this workspace and apply any that are relevant to completing the user's request at the highest level of efficiency, quality, and completeness. When skills overlap in scope, assess the overlapping skills in greater detail and autonomously determine which is the best match for the project or the specific request — do not prompt the user to resolve overlaps.
326
+ OPENING="Assess the following skills available in this workspace and apply any that are relevant to completing the user's request at the highest level of efficiency, quality, and completeness. When skills overlap in scope, assess the overlapping skills in greater detail and autonomously determine which is the best match for the project or the specific request — do not prompt the user to resolve overlaps.
278
327
 
279
328
  CRITICAL REQUIREMENT: Before applying any skill, you MUST use the Read tool to read the full contents of the skill file at the provided path. Do not assume the skill's behavior from its title or description alone.
280
329
 
@@ -285,10 +334,12 @@ Examples:
285
334
  - \"Delegate to the \`frontend-designer\` subagent using \`ui-ux-pro-max/SKILL.md\` to build the polished component.\"
286
335
  - \"Utilize \`supabase-postgres-best-practices/SKILL.md\` when designing the database schema for the user profiles.\""
287
336
 
288
- is_update=false
289
- [[ -f "$OUTPUT_FILE" ]] && is_update=true
337
+ mkdir -p "$GLOBAL_COMMANDS_DIR"
290
338
 
291
- cat > "$OUTPUT_FILE" <<EOF
339
+ is_update=false
340
+ [[ -f "$OUTPUT_FILE" ]] && is_update=true
341
+
342
+ cat > "$OUTPUT_FILE" <<EOF
292
343
  # Skill Tags Command
293
344
 
294
345
  <!-- Auto-generated by sync.sh (skill-tags) v${VERSION} — do not edit manually -->
@@ -299,20 +350,18 @@ ${OPENING}
299
350
  $(cat "$SKILLS_TEMP")
300
351
  EOF
301
352
 
302
- # ─── Generate category files (if config exists) ────────────────────────────────
303
-
304
- generate_category_files
353
+ generate_category_files
305
354
 
306
- # ─── Summary ───────────────────────────────────────────────────────────────────
355
+ printf "\n"
356
+ if [[ "$is_update" == "true" ]]; then
357
+ printf " ✓ Updated: %s\n" "${OUTPUT_FILE/#$HOME/~}"
358
+ else
359
+ printf " ✓ Generated: %s\n" "${OUTPUT_FILE/#$HOME/~}"
360
+ fi
361
+ printf " Skills: %d indexed\n" "$count_found"
362
+ if [[ $count_dupes -gt 0 ]]; then
363
+ printf " Duplicates: %d skipped (covered by higher-priority source)\n" "$count_dupes"
364
+ fi
365
+ printf "\n Tip: type @skill-tags.md in Cursor chat to load the full skills reference.\n\n"
307
366
 
308
- printf "\n"
309
- if [[ "$is_update" == "true" ]]; then
310
- printf " ✓ Updated: %s\n" "${OUTPUT_FILE/#$HOME/~}"
311
- else
312
- printf " ✓ Generated: %s\n" "${OUTPUT_FILE/#$HOME/~}"
313
- fi
314
- printf " Skills: %d indexed\n" "$count_found"
315
- if [[ $count_dupes -gt 0 ]]; then
316
- printf " Duplicates: %d skipped (covered by higher-priority source)\n" "$count_dupes"
317
367
  fi
318
- printf "\n Tip: type @skill-tags.md in Cursor chat to load the full skills reference.\n\n"