ai-omni-skills 1.2.21 → 1.2.22
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/CHANGELOG.md +18 -0
- package/README.md +67 -0
- package/cli.js +45 -0
- package/lib/create-skill.js +180 -0
- package/lib/doctor.js +15 -1
- package/lib/security.js +129 -0
- package/lib/setup.js +29 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.22] - 2026-06-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **NVIDIA SkillSpector integration** — security scanning for AI skills
|
|
7
|
+
- `omni-skills security` — check SkillSpector status
|
|
8
|
+
- `omni-skills security scan` — scan skills for 64 vulnerability patterns
|
|
9
|
+
- Setup asks to install SkillSpector (default: yes)
|
|
10
|
+
- Doctor checks SkillSpector installation status
|
|
11
|
+
- README documents SkillSpector with NVIDIA reference
|
|
12
|
+
- **Skill creation wizard** — create skills directly without going through an agent
|
|
13
|
+
- `omni-skills create [name]` — interactive wizard with SKILL.md template
|
|
14
|
+
- `omni-skills create from <file>` — convert existing file to skill
|
|
15
|
+
- **Help hint** — quick-start banner at bottom of `omni-skills help`
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Rename CLI command from `skills` to `omni-skills` (`skills` kept as backward-compatible alias)
|
|
19
|
+
- Update all documentation examples to use `omni-skills`
|
|
20
|
+
|
|
3
21
|
## [1.2.20] - 2026-06-22
|
|
4
22
|
|
|
5
23
|
### Added
|
package/README.md
CHANGED
|
@@ -167,6 +167,9 @@ Tools with MCP support get the `skills` MCP server registered automatically. Too
|
|
|
167
167
|
| `omni-skills doctor` | Health check: verify symlinks, config, indexes, and skill counts. |
|
|
168
168
|
| `omni-skills report [--enhance]` | Usage statistics and heuristic improvement tips. |
|
|
169
169
|
| `omni-skills init [--dry-run]` | Interactive setup: scan, classify, route, wire. |
|
|
170
|
+
| `omni-skills security [scan]` | Check/install NVIDIA SkillSpector. Scan skills for vulnerabilities. |
|
|
171
|
+
| `omni-skills create [name]` | Create a new skill with interactive wizard. |
|
|
172
|
+
| `omni-skills create from <file>` | Convert an existing file into a skill. |
|
|
170
173
|
| `omni-skills help` | Show help. |
|
|
171
174
|
|
|
172
175
|
---
|
|
@@ -264,6 +267,70 @@ Workflows live in your private repo under `workflows/` — they are personal, no
|
|
|
264
267
|
|
|
265
268
|
---
|
|
266
269
|
|
|
270
|
+
## 🔒 Security Scanning (NVIDIA SkillSpector)
|
|
271
|
+
|
|
272
|
+
Research shows **26.1% of AI skills contain vulnerabilities** and **5.2% show likely malicious intent**. Omni Skills integrates with [NVIDIA SkillSpector](https://github.com/NVIDIA/skillspector) to scan your skills before you install them.
|
|
273
|
+
|
|
274
|
+
SkillSpector detects **64 vulnerability patterns** across **16 categories**:
|
|
275
|
+
- Prompt injection, data exfiltration, privilege escalation
|
|
276
|
+
- Supply chain attacks, malware, credential harvesting
|
|
277
|
+
- Excessive agency, tool misuse, system prompt leakage
|
|
278
|
+
|
|
279
|
+
### Quick Start
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
# Check SkillSpector status
|
|
283
|
+
omni-skills security
|
|
284
|
+
|
|
285
|
+
# Scan all skills in your canonical store
|
|
286
|
+
omni-skills security scan
|
|
287
|
+
|
|
288
|
+
# Scan without LLM (faster, static analysis only)
|
|
289
|
+
omni-skills security scan --no-llm
|
|
290
|
+
|
|
291
|
+
# Scan a specific skill directory
|
|
292
|
+
omni-skills security scan ~/my-skills-private/clean-code
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Install SkillSpector
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
git clone https://github.com/NVIDIA/skillspector.git
|
|
299
|
+
cd skillspector
|
|
300
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
301
|
+
pip install -e .
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Or use Docker:
|
|
305
|
+
```bash
|
|
306
|
+
docker run --rm -v "$PWD:/scan" ghcr.io/nvidia/skillspector scan /scan
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## ✨ Creating New Skills
|
|
312
|
+
|
|
313
|
+
Instead of creating skills through an agent and then moving them, create them directly:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Interactive wizard
|
|
317
|
+
omni-skills create
|
|
318
|
+
|
|
319
|
+
# With name and description
|
|
320
|
+
omni-skills create refine-requests "Refine ticket acceptance criteria"
|
|
321
|
+
|
|
322
|
+
# Convert an existing file into a skill
|
|
323
|
+
omni-skills create from ./my-instructions.md
|
|
324
|
+
|
|
325
|
+
# Then index and sync
|
|
326
|
+
omni-skills index
|
|
327
|
+
omni-skills sync all
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
This creates a `SKILL.md` with proper frontmatter in your private skills store.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
267
334
|
## How to Store Your Skills
|
|
268
335
|
|
|
269
336
|
This toolkit is **code only**. Your skills live in a separate repository (or two) that you control.
|
package/cli.js
CHANGED
|
@@ -68,6 +68,15 @@ Subcommands:
|
|
|
68
68
|
Interactive seeker: scan tool skills dirs and known agent locations for
|
|
69
69
|
new skills, classify them public/private/skip, then sync and index.
|
|
70
70
|
--dry-run prints recommendations without prompting or writing.
|
|
71
|
+
security [scan [path] [--no-llm]]
|
|
72
|
+
Check security status of NVIDIA SkillSpector.
|
|
73
|
+
Scan skills for vulnerabilities (prompt injection, data exfiltration,
|
|
74
|
+
malware, etc.). Requires SkillSpector installation.
|
|
75
|
+
create [name] [description]
|
|
76
|
+
Create a new skill with interactive wizard.
|
|
77
|
+
Creates SKILL.md template in your private skills store.
|
|
78
|
+
create from <path> [name]
|
|
79
|
+
Convert an existing file into a skill.
|
|
71
80
|
help Show this help message
|
|
72
81
|
|
|
73
82
|
Config resolves from $SKILLS_CONFIG, then ~/.config/skills/config.json, then the
|
|
@@ -181,6 +190,42 @@ async function main() {
|
|
|
181
190
|
await initSkills(loadConfig(), { dryRun: flags.dryRun });
|
|
182
191
|
break;
|
|
183
192
|
}
|
|
193
|
+
case 'security': {
|
|
194
|
+
const { showSecurityStatus, runSkillSpectorScan, showSecurityBanner } = await import('./lib/security.js');
|
|
195
|
+
const config = loadConfig();
|
|
196
|
+
const subcmd = positional[0];
|
|
197
|
+
if (subcmd === 'scan') {
|
|
198
|
+
const target = positional[1] || config.privatePath || config.publicPath || process.cwd();
|
|
199
|
+
showSecurityBanner();
|
|
200
|
+
const output = runSkillSpectorScan(target, {
|
|
201
|
+
noLLM: rest.includes('--no-llm'),
|
|
202
|
+
format: rest.find(a => a.startsWith('--format='))?.split('=')[1],
|
|
203
|
+
output: rest.find(a => a.startsWith('--output='))?.split('=')[1],
|
|
204
|
+
verbose: rest.includes('-V') || rest.includes('--verbose'),
|
|
205
|
+
});
|
|
206
|
+
if (output) console.log(output);
|
|
207
|
+
} else {
|
|
208
|
+
showSecurityStatus();
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case 'create': {
|
|
213
|
+
const { createSkill, createSkillFromFile } = await import('./lib/create-skill.js');
|
|
214
|
+
const config = loadConfig();
|
|
215
|
+
const subcmd = positional[0];
|
|
216
|
+
if (subcmd === 'from' && positional[1]) {
|
|
217
|
+
await createSkillFromFile(config, positional[1], {
|
|
218
|
+
name: positional[2],
|
|
219
|
+
});
|
|
220
|
+
} else {
|
|
221
|
+
await createSkill(config, {
|
|
222
|
+
name: positional[0],
|
|
223
|
+
description: positional[1],
|
|
224
|
+
targetDir: flags.privatePath || config.privatePath,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
184
229
|
case 'help':
|
|
185
230
|
case '-h':
|
|
186
231
|
case undefined:
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import readline from 'node:readline';
|
|
4
|
+
|
|
5
|
+
const SKILL_TEMPLATE = `---
|
|
6
|
+
name: {{NAME}}
|
|
7
|
+
description: {{DESCRIPTION}}
|
|
8
|
+
triggers: {{TRIGGERS}}
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# {{NAME}}
|
|
12
|
+
|
|
13
|
+
{{DESCRIPTION}}
|
|
14
|
+
|
|
15
|
+
## When to Use
|
|
16
|
+
|
|
17
|
+
- Trigger condition 1
|
|
18
|
+
- Trigger condition 2
|
|
19
|
+
|
|
20
|
+
## Instructions
|
|
21
|
+
|
|
22
|
+
1. Step one
|
|
23
|
+
2. Step two
|
|
24
|
+
3. Step three
|
|
25
|
+
|
|
26
|
+
## Notes
|
|
27
|
+
|
|
28
|
+
- Add any important notes or edge cases here
|
|
29
|
+
- Keep this section updated as you refine the skill
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
function ask(rl, question) {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
rl.question(question, resolve);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function createSkill(config, options = {}) {
|
|
39
|
+
const { privatePath } = config;
|
|
40
|
+
if (!privatePath) {
|
|
41
|
+
console.error('❌ No privatePath configured. Run `omni-skills setup` first.');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const isTty = process.stdin.isTTY && process.stdout.isTTY;
|
|
46
|
+
const rl = isTty ? readline.createInterface({ input: process.stdin, output: process.stdout }) : null;
|
|
47
|
+
|
|
48
|
+
// Determine target directory
|
|
49
|
+
let targetDir = options.targetDir || privatePath;
|
|
50
|
+
if (!existsSync(targetDir)) {
|
|
51
|
+
console.error(`❌ Target directory does not exist: ${targetDir}`);
|
|
52
|
+
console.log('Run `omni-skills setup` to configure your skills directory.');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let skillName = options.name;
|
|
57
|
+
let description = options.description;
|
|
58
|
+
let triggers = options.triggers;
|
|
59
|
+
let openEditor = options.openEditor ?? true;
|
|
60
|
+
|
|
61
|
+
if (isTty && !skillName) {
|
|
62
|
+
skillName = (await ask(rl, 'Skill name (kebab-case, e.g. "refine-requests"): ')).trim();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!skillName) {
|
|
66
|
+
console.error('❌ Skill name is required.');
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Validate skill name
|
|
71
|
+
const validName = /^[a-z0-9-]+$/.test(skillName);
|
|
72
|
+
if (!validName) {
|
|
73
|
+
console.error('❌ Skill name must be kebab-case (lowercase letters, numbers, hyphens only).');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const skillDir = join(targetDir, skillName);
|
|
78
|
+
if (existsSync(skillDir)) {
|
|
79
|
+
console.error(`❌ Skill "${skillName}" already exists at ${skillDir}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isTty && !description) {
|
|
84
|
+
description = (await ask(rl, 'Description (one sentence, what this skill does): ')).trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!description) {
|
|
88
|
+
description = `Use this skill when working with ${skillName}.`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (isTty && !triggers) {
|
|
92
|
+
triggers = (await ask(rl, 'Trigger keywords (comma-separated, e.g. "refine, clarify, review"): ')).trim();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!triggers) {
|
|
96
|
+
triggers = skillName;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Create skill directory and file
|
|
100
|
+
mkdirSync(skillDir, { recursive: true });
|
|
101
|
+
const skillFile = join(skillDir, 'SKILL.md');
|
|
102
|
+
|
|
103
|
+
const content = SKILL_TEMPLATE
|
|
104
|
+
.replace(/{{NAME}}/g, skillName)
|
|
105
|
+
.replace(/{{DESCRIPTION}}/g, description)
|
|
106
|
+
.replace(/{{TRIGGERS}}/g, triggers);
|
|
107
|
+
|
|
108
|
+
writeFileSync(skillFile, content, 'utf8');
|
|
109
|
+
|
|
110
|
+
console.log(`✅ Created skill: ${skillName}`);
|
|
111
|
+
console.log(` Path: ${skillFile}`);
|
|
112
|
+
console.log('');
|
|
113
|
+
console.log('Next steps:');
|
|
114
|
+
console.log(' 1. Edit SKILL.md to add your instructions');
|
|
115
|
+
console.log(' 2. Run `omni-skills index` to regenerate the skill index');
|
|
116
|
+
console.log(' 3. Run `omni-skills sync all` to wire into all tools');
|
|
117
|
+
console.log('');
|
|
118
|
+
|
|
119
|
+
if (isTty && openEditor) {
|
|
120
|
+
const answer = (await ask(rl, 'Open in editor? [y/n] (default: y): ')).trim().toLowerCase();
|
|
121
|
+
if (answer === '' || answer === 'y' || answer === 'yes') {
|
|
122
|
+
const { execSync } = await import('node:child_process');
|
|
123
|
+
const editor = process.env.EDITOR || 'code';
|
|
124
|
+
try {
|
|
125
|
+
execSync(`${editor} "${skillFile}"`, { stdio: 'inherit' });
|
|
126
|
+
} catch {
|
|
127
|
+
console.log(`Could not open editor. Edit manually: ${skillFile}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (rl) rl.close();
|
|
133
|
+
return { skillName, skillDir, skillFile };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function createSkillFromFile(config, sourcePath, options = {}) {
|
|
137
|
+
if (!existsSync(sourcePath)) {
|
|
138
|
+
console.error(`❌ Source file does not exist: ${sourcePath}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const { privatePath } = config;
|
|
143
|
+
if (!privatePath) {
|
|
144
|
+
console.error('❌ No privatePath configured. Run `omni-skills setup` first.');
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const sourceName = sourcePath.split('/').pop().replace(/\.md$/, '');
|
|
149
|
+
const skillName = options.name || sourceName.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
150
|
+
|
|
151
|
+
const skillDir = join(privatePath, skillName);
|
|
152
|
+
if (existsSync(skillDir)) {
|
|
153
|
+
console.error(`❌ Skill "${skillName}" already exists.`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
mkdirSync(skillDir, { recursive: true });
|
|
158
|
+
const skillFile = join(skillDir, 'SKILL.md');
|
|
159
|
+
|
|
160
|
+
let content = readFileSync(sourcePath, 'utf8');
|
|
161
|
+
|
|
162
|
+
// If content doesn't have frontmatter, add it
|
|
163
|
+
if (!content.trim().startsWith('---')) {
|
|
164
|
+
const description = options.description || `Converted from ${sourceName}`;
|
|
165
|
+
content = `---\nname: ${skillName}\ndescription: ${description}\n---\n\n${content}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
writeFileSync(skillFile, content, 'utf8');
|
|
169
|
+
|
|
170
|
+
console.log(`✅ Created skill from file: ${skillName}`);
|
|
171
|
+
console.log(` Source: ${sourcePath}`);
|
|
172
|
+
console.log(` Path: ${skillFile}`);
|
|
173
|
+
console.log('');
|
|
174
|
+
console.log('Next steps:');
|
|
175
|
+
console.log(' 1. Review and edit SKILL.md');
|
|
176
|
+
console.log(' 2. Run `omni-skills index` to regenerate the skill index');
|
|
177
|
+
console.log(' 3. Run `omni-skills sync all` to wire into all tools');
|
|
178
|
+
|
|
179
|
+
return { skillName, skillDir, skillFile };
|
|
180
|
+
}
|
package/lib/doctor.js
CHANGED
|
@@ -246,6 +246,20 @@ export async function runDoctor(config) {
|
|
|
246
246
|
}
|
|
247
247
|
console.log('');
|
|
248
248
|
|
|
249
|
+
// 6. Security check (SkillSpector)
|
|
250
|
+
console.log('[security]');
|
|
251
|
+
const { isSkillSpectorInstalled, findSkillSpector } = await import('./security.js');
|
|
252
|
+
if (isSkillSpectorInstalled()) {
|
|
253
|
+
const path = findSkillSpector();
|
|
254
|
+
console.log(` ✓ NVIDIA SkillSpector installed (${path})`);
|
|
255
|
+
console.log(' Run `omni-skills security scan` to check skills for vulnerabilities');
|
|
256
|
+
} else {
|
|
257
|
+
console.log(' ⚠ NVIDIA SkillSpector not installed (optional)');
|
|
258
|
+
console.log(' Detects 64 vulnerability patterns in AI skills');
|
|
259
|
+
console.log(' Install: https://github.com/NVIDIA/skillspector');
|
|
260
|
+
}
|
|
261
|
+
console.log('');
|
|
262
|
+
|
|
249
263
|
// Summary
|
|
250
264
|
if (issues.length === 0) {
|
|
251
265
|
console.log('✓ All checks passed. The skills ecosystem is healthy.');
|
|
@@ -254,6 +268,6 @@ export async function runDoctor(config) {
|
|
|
254
268
|
for (const issue of issues) {
|
|
255
269
|
console.log(issue);
|
|
256
270
|
}
|
|
257
|
-
console.log('\nRun `skills sync all` to fix most symlink issues.');
|
|
271
|
+
console.log('\nRun `omni-skills sync all` to fix most symlink issues.');
|
|
258
272
|
}
|
|
259
273
|
}
|
package/lib/security.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
|
|
6
|
+
const SKILLSPECTOR_PATHS = [
|
|
7
|
+
'skillspector',
|
|
8
|
+
join(homedir(), '.local', 'bin', 'skillspector'),
|
|
9
|
+
'/usr/local/bin/skillspector',
|
|
10
|
+
'/usr/bin/skillspector',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export function findSkillSpector() {
|
|
14
|
+
for (const path of SKILLSPECTOR_PATHS) {
|
|
15
|
+
try {
|
|
16
|
+
execSync(`which ${path}`, { stdio: 'ignore' });
|
|
17
|
+
return path;
|
|
18
|
+
} catch {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isSkillSpectorInstalled() {
|
|
26
|
+
return findSkillSpector() !== null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getSkillSpectorVersion() {
|
|
30
|
+
const path = findSkillSpector();
|
|
31
|
+
if (!path) return null;
|
|
32
|
+
try {
|
|
33
|
+
const output = execSync(`${path} --version`, { encoding: 'utf8', timeout: 5000 });
|
|
34
|
+
return output.trim();
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function runSkillSpectorScan(targetPath, options = {}) {
|
|
41
|
+
const path = findSkillSpector();
|
|
42
|
+
if (!path) {
|
|
43
|
+
console.error('❌ SkillSpector not found.');
|
|
44
|
+
console.log('');
|
|
45
|
+
console.log('🔒 NVIDIA SkillSpector — Security Scanner for AI Skills');
|
|
46
|
+
console.log(' Detects 64 vulnerability patterns across 16 categories');
|
|
47
|
+
console.log(' (prompt injection, data exfiltration, privilege escalation, etc.)');
|
|
48
|
+
console.log('');
|
|
49
|
+
console.log('Install:');
|
|
50
|
+
console.log(' git clone https://github.com/NVIDIA/skillspector.git');
|
|
51
|
+
console.log(' cd skillspector');
|
|
52
|
+
console.log(' python3 -m venv .venv && source .venv/bin/activate');
|
|
53
|
+
console.log(' pip install -e .');
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log('Or via Docker:');
|
|
56
|
+
console.log(' docker run --rm -v "$PWD:/scan" ghcr.io/nvidia/skillspector scan /scan');
|
|
57
|
+
console.log('');
|
|
58
|
+
console.log('📎 https://github.com/NVIDIA/skillspector');
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const args = ['scan', targetPath];
|
|
63
|
+
if (options.noLLM) args.push('--no-llm');
|
|
64
|
+
if (options.format) args.push('--format', options.format);
|
|
65
|
+
if (options.output) args.push('--output', options.output);
|
|
66
|
+
if (options.verbose) args.push('-V');
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const output = execSync(`${path} ${args.join(' ')}`, {
|
|
70
|
+
encoding: 'utf8',
|
|
71
|
+
timeout: 120000,
|
|
72
|
+
stdio: 'pipe',
|
|
73
|
+
});
|
|
74
|
+
return output;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (err.stdout) return err.stdout;
|
|
77
|
+
if (err.stderr) return err.stderr;
|
|
78
|
+
return err.message;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function showSecurityStatus() {
|
|
83
|
+
const path = findSkillSpector();
|
|
84
|
+
if (path) {
|
|
85
|
+
const version = getSkillSpectorVersion();
|
|
86
|
+
console.log('✅ NVIDIA SkillSpector is installed');
|
|
87
|
+
if (version) console.log(` Version: ${version}`);
|
|
88
|
+
console.log(` Path: ${path}`);
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log('Usage:');
|
|
91
|
+
console.log(' omni-skills security scan # Scan all skills');
|
|
92
|
+
console.log(' omni-skills security scan --dir ./ # Scan specific directory');
|
|
93
|
+
console.log(' omni-skills security scan --no-llm # Static analysis only (faster)');
|
|
94
|
+
} else {
|
|
95
|
+
console.log('⚠️ NVIDIA SkillSpector is not installed');
|
|
96
|
+
console.log('');
|
|
97
|
+
console.log('🔒 Why install it?');
|
|
98
|
+
console.log(' • Scans AI skills for 64 vulnerability patterns');
|
|
99
|
+
console.log(' • Detects prompt injection, data exfiltration, malware');
|
|
100
|
+
console.log(' • 26.1% of skills contain vulnerabilities (research-backed)');
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log('Install:');
|
|
103
|
+
console.log(' git clone https://github.com/NVIDIA/skillspector.git');
|
|
104
|
+
console.log(' cd skillspector');
|
|
105
|
+
console.log(' python3 -m venv .venv && source .venv/bin/activate');
|
|
106
|
+
console.log(' pip install -e .');
|
|
107
|
+
console.log('');
|
|
108
|
+
console.log('Or use Docker:');
|
|
109
|
+
console.log(' docker run --rm -v "$PWD:/scan" ghcr.io/nvidia/skillspector scan /scan');
|
|
110
|
+
console.log('');
|
|
111
|
+
console.log('📎 https://github.com/NVIDIA/skillspector');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function showSecurityBanner() {
|
|
116
|
+
console.log('');
|
|
117
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
118
|
+
console.log(' 🔒 NVIDIA SkillSpector — Security for AI Skills');
|
|
119
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
120
|
+
console.log('');
|
|
121
|
+
console.log('Research shows 26.1% of skills contain vulnerabilities.');
|
|
122
|
+
console.log('SkillSpector detects 64 patterns across 16 categories:');
|
|
123
|
+
console.log(' • Prompt injection, data exfiltration, privilege escalation');
|
|
124
|
+
console.log(' • Supply chain attacks, malware, credential harvesting');
|
|
125
|
+
console.log(' • Excessive agency, tool misuse, system prompt leakage');
|
|
126
|
+
console.log('');
|
|
127
|
+
console.log('📎 https://github.com/NVIDIA/skillspector');
|
|
128
|
+
console.log('');
|
|
129
|
+
}
|
package/lib/setup.js
CHANGED
|
@@ -218,7 +218,7 @@ export async function runSetup({ publicPath, privatePath, toolkitDir } = {}) {
|
|
|
218
218
|
type: 'stale-template',
|
|
219
219
|
project: proj.name,
|
|
220
220
|
count: staleTemplates.length,
|
|
221
|
-
action: `skills classify ${proj.path} --dry-run`,
|
|
221
|
+
action: `omni-skills classify ${proj.path} --dry-run`,
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
224
|
}
|
|
@@ -236,7 +236,7 @@ export async function runSetup({ publicPath, privatePath, toolkitDir } = {}) {
|
|
|
236
236
|
suggestions.push({
|
|
237
237
|
type: 'orphaned-skills',
|
|
238
238
|
count: orphanedSkills,
|
|
239
|
-
action: 'skills check --move --dry-run',
|
|
239
|
+
action: 'omni-skills check --move --dry-run',
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
242
|
|
|
@@ -299,7 +299,28 @@ export async function runSetup({ publicPath, privatePath, toolkitDir } = {}) {
|
|
|
299
299
|
console.log(` ✓ Created SHARED.md in ${resolvedPublic}/`);
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
// 8.
|
|
302
|
+
// 8. Ask about SkillSpector (default: yes)
|
|
303
|
+
let installSkillSpector = true;
|
|
304
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
305
|
+
const readline = await import('node:readline');
|
|
306
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
307
|
+
const answer = await new Promise((resolve) => {
|
|
308
|
+
rl.question('\n🔒 NVIDIA SkillSpector — Security Scanner for AI Skills\n Detects 64 vulnerability patterns (prompt injection, data exfiltration, malware)\n Research: 26.1% of skills contain vulnerabilities\n Install? [Y/n] (default: yes): ', resolve);
|
|
309
|
+
});
|
|
310
|
+
rl.close();
|
|
311
|
+
installSkillSpector = answer.trim().toLowerCase() !== 'n' && answer.trim().toLowerCase() !== 'no';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (installSkillSpector) {
|
|
315
|
+
console.log('\n📎 SkillSpector: https://github.com/NVIDIA/skillspector');
|
|
316
|
+
console.log(' Install: git clone https://github.com/NVIDIA/skillspector.git');
|
|
317
|
+
console.log(' cd skillspector && python3 -m venv .venv && source .venv/bin/activate');
|
|
318
|
+
console.log(' pip install -e .');
|
|
319
|
+
console.log(' Then run: omni-skills security scan');
|
|
320
|
+
}
|
|
321
|
+
console.log('');
|
|
322
|
+
|
|
323
|
+
// 9. Generate config.json
|
|
303
324
|
console.log('\nGenerating config...');
|
|
304
325
|
const configDir = absPath('~/.config/skills');
|
|
305
326
|
if (!existsSync(configDir)) {
|
|
@@ -329,9 +350,10 @@ export async function runSetup({ publicPath, privatePath, toolkitDir } = {}) {
|
|
|
329
350
|
console.log(' 2. Run `skills classify <path>` to sort instruction files');
|
|
330
351
|
console.log(' 3. Run `skills check --move` to rescue orphaned skills');
|
|
331
352
|
}
|
|
332
|
-
console.log(' Run `skills index` to generate indexes');
|
|
333
|
-
console.log(' Run `skills sync all` to wire into your AI tools');
|
|
334
|
-
console.log(' Run `skills doctor` to verify everything is healthy');
|
|
353
|
+
console.log(' Run `omni-skills index` to generate indexes');
|
|
354
|
+
console.log(' Run `omni-skills sync all` to wire into your AI tools');
|
|
355
|
+
console.log(' Run `omni-skills doctor` to verify everything is healthy');
|
|
356
|
+
console.log(' Run `omni-skills security` to check security status');
|
|
335
357
|
console.log(' Run `node verify.js` to run the full verification suite');
|
|
336
358
|
console.log('');
|
|
337
359
|
console.log('For more info: https://github.com/moatazhamada/ai-omni-skills');
|
|
@@ -513,7 +535,7 @@ function generateConfig(answers) {
|
|
|
513
535
|
sharedFile: join(answers.publicPath, 'SHARED.md'),
|
|
514
536
|
indexTargets: [answers.publicPath, answers.privatePath],
|
|
515
537
|
usageLog: "~/.config/skills/usage.jsonl",
|
|
516
|
-
mcpServerName: "skills",
|
|
538
|
+
mcpServerName: "omni-skills",
|
|
517
539
|
mcpCommand: "node",
|
|
518
540
|
mcpArgs: [join(answers.toolkitDir, "cli.js"), "mcp"],
|
|
519
541
|
hooks: [
|