Skip to content

Commit d3b2912

Browse files
Ubuntuclaude
authored andcommitted
Enhance all 45 skills per SKILL_AUDIT.md 7-rule evaluation
112 mandatory actions executed across 10 domains: - R4 File Structure: Created /references/, /scripts/, /templates/ dirs across all skills. Extracted inline content (regex patterns, CWE maps, framework tables, report templates) into structured files (~75 new files) - R2 Verification: Added falsifiable tests to 33 skills - R5 Gotchas: Added/strengthened gotchas on 15 skills (FP patterns, precision traps, exploit lessons) - R3 Elegance: Deduplicated owasp-top-10-web↔secure-code-review, agentic-top-10↔agent-security, refactored iam-review as orchestrator - R1 System Layer: Added executable Grep/Glob patterns to llm-top-10 (57 patterns), zero-trust-assessment, prompt-injection, agentic-top-10 - R7 Subagent: Added parallelization markers to 12 skills Also adds npm package (@unitoneai/skills), SKILL_AUDIT.md, and .gitignore updates for node_modules. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ec4f431 commit d3b2912

167 files changed

Lines changed: 7332 additions & 2075 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ Thumbs.db
88
*.swp
99
*.swo
1010

11+
# Node
12+
node_modules/
13+
1114
# Plan file (internal reference, not published)
1215
skillsrepo.md

.npmignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.claude/
2+
.git/
3+
.github/
4+
.gitignore
5+
CONTRIBUTING.md
6+
SECURITY.md
7+
skillsrepo.md
8+
*.swp
9+
*.swo

SKILL_AUDIT.md

Lines changed: 328 additions & 0 deletions
Large diffs are not rendered by default.

bin/skills.mjs

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
#!/usr/bin/env node
2+
3+
import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, readFileSync } from 'node:fs';
4+
import { join, resolve, dirname } from 'node:path';
5+
import { fileURLToPath } from 'node:url';
6+
import { createInterface } from 'node:readline';
7+
8+
const __filename = fileURLToPath(import.meta.url);
9+
const DATA_ROOT = resolve(dirname(__filename), '..');
10+
const CWD = process.cwd();
11+
const VERSION = JSON.parse(readFileSync(join(DATA_ROOT, 'package.json'), 'utf8')).version;
12+
13+
const TOOLS = {
14+
'claude-code': {
15+
name: 'Claude Code',
16+
detect: () => existsSync(join(CWD, '.claude')),
17+
skillsDir: '.claude/skills',
18+
},
19+
'gemini-cli': {
20+
name: 'Gemini CLI',
21+
detect: () => existsSync(join(CWD, '.gemini')),
22+
skillsDir: '.gemini/skills',
23+
},
24+
cursor: {
25+
name: 'Cursor',
26+
detect: () => existsSync(join(CWD, '.cursor')) || existsSync(join(CWD, '.cursorrules')),
27+
skillsDir: '.cursor/rules/security-skills',
28+
},
29+
codex: {
30+
name: 'Codex CLI',
31+
detect: () => existsSync(join(CWD, '.codex')),
32+
skillsDir: '.codex/skills',
33+
},
34+
generic: {
35+
name: 'Generic (./security-skills/)',
36+
detect: () => true,
37+
skillsDir: 'security-skills',
38+
},
39+
};
40+
41+
function copyRecursive(src, dest) {
42+
let count = 0;
43+
mkdirSync(dest, { recursive: true });
44+
for (const entry of readdirSync(src)) {
45+
const srcPath = join(src, entry);
46+
const destPath = join(dest, entry);
47+
if (statSync(srcPath).isDirectory()) {
48+
count += copyRecursive(srcPath, destPath);
49+
} else {
50+
copyFileSync(srcPath, destPath);
51+
count++;
52+
}
53+
}
54+
return count;
55+
}
56+
57+
function prompt(question) {
58+
const rl = createInterface({ input: process.stdin, output: process.stdout });
59+
return new Promise((resolve) => {
60+
rl.question(question, (answer) => {
61+
rl.close();
62+
resolve(answer.trim());
63+
});
64+
});
65+
}
66+
67+
function printUsage() {
68+
console.log(`
69+
@unitone/skills v${VERSION}
70+
45 security skills for AI coding agents
71+
72+
Usage:
73+
npx @unitone/skills init [options] Install skills into your project
74+
npx @unitone/skills list List all available skills
75+
npx @unitone/skills --version Show version
76+
npx @unitone/skills --help Show this help
77+
78+
Options:
79+
-y, --yes Auto-detect tools and install without prompting
80+
--force Overwrite existing skills directory
81+
--tool <name> Install for a specific tool (claude-code, gemini-cli, cursor, codex, generic)
82+
`);
83+
}
84+
85+
function listSkills() {
86+
const indexPath = join(DATA_ROOT, 'index.yaml');
87+
const content = readFileSync(indexPath, 'utf8');
88+
const lines = content.split('\n');
89+
90+
console.log('\n Security Skills\n');
91+
92+
// Parse skills into structured data, skip roles
93+
const skills = [];
94+
let current = null;
95+
let inRoles = false;
96+
for (const line of lines) {
97+
if (line.match(/^roles:/)) { inRoles = true; continue; }
98+
if (inRoles) continue;
99+
100+
const idMatch = line.match(/^\s+-\s+id:\s+(.+)/);
101+
if (idMatch) {
102+
current = { id: idMatch[1].trim(), name: '', domain: '' };
103+
skills.push(current);
104+
continue;
105+
}
106+
if (!current) continue;
107+
const nameMatch = line.match(/^\s+name:\s+"([^"]+)"/);
108+
if (nameMatch) current.name = nameMatch[1];
109+
const fileMatch = line.match(/^\s+file:\s+skills\/([^/]+)\//);
110+
if (fileMatch) current.domain = fileMatch[1];
111+
}
112+
113+
let currentDomain = '';
114+
for (const skill of skills) {
115+
if (!skill.domain || !skill.name) continue;
116+
if (skill.domain !== currentDomain) {
117+
currentDomain = skill.domain;
118+
console.log(`\n ${currentDomain}`);
119+
console.log(` ${'─'.repeat(currentDomain.length)}`);
120+
}
121+
console.log(` ${skill.name}`);
122+
}
123+
console.log('');
124+
}
125+
126+
async function init(args) {
127+
const autoYes = args.includes('-y') || args.includes('--yes');
128+
const force = args.includes('--force');
129+
const toolIdx = args.indexOf('--tool');
130+
const specificTool = toolIdx !== -1 ? args[toolIdx + 1] : null;
131+
132+
console.log(`\n @unitone/skills v${VERSION}\n`);
133+
134+
let selectedKeys = [];
135+
136+
if (specificTool) {
137+
if (!TOOLS[specificTool]) {
138+
console.error(` Unknown tool: ${specificTool}`);
139+
console.error(` Available: ${Object.keys(TOOLS).join(', ')}`);
140+
process.exit(1);
141+
}
142+
selectedKeys = [specificTool];
143+
} else {
144+
const detected = Object.entries(TOOLS)
145+
.filter(([key, cfg]) => key !== 'generic' && cfg.detect())
146+
.map(([key]) => key);
147+
148+
if (autoYes) {
149+
selectedKeys = detected.length > 0 ? detected : ['generic'];
150+
} else {
151+
const entries = Object.entries(TOOLS);
152+
console.log(' Available targets:\n');
153+
entries.forEach(([key, cfg], i) => {
154+
const tag = key !== 'generic' && cfg.detect() ? ' (detected)' : '';
155+
console.log(` [${i + 1}] ${cfg.name}${tag}`);
156+
});
157+
158+
const answer = await prompt('\n Install for which targets? (numbers, comma-separated, or "all"): ');
159+
160+
if (answer.toLowerCase() === 'all') {
161+
selectedKeys = Object.keys(TOOLS);
162+
} else {
163+
const nums = answer.split(',').map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n));
164+
selectedKeys = nums.map((n) => entries[n - 1]?.[0]).filter(Boolean);
165+
}
166+
167+
if (selectedKeys.length === 0) {
168+
console.log(' No targets selected. Exiting.\n');
169+
process.exit(0);
170+
}
171+
}
172+
}
173+
174+
const skillsSrc = join(DATA_ROOT, 'skills');
175+
const rolesSrc = join(DATA_ROOT, 'roles');
176+
const indexSrc = join(DATA_ROOT, 'index.yaml');
177+
178+
for (const key of selectedKeys) {
179+
const cfg = TOOLS[key];
180+
const targetDir = join(CWD, cfg.skillsDir);
181+
182+
if (existsSync(targetDir) && !force) {
183+
const answer = autoYes ? 'y' : await prompt(` ${targetDir} already exists. Overwrite? (y/N): `);
184+
if (answer.toLowerCase() !== 'y') {
185+
console.log(` Skipped ${cfg.name}`);
186+
continue;
187+
}
188+
}
189+
190+
const skillsTarget = join(targetDir, 'skills');
191+
const rolesTarget = join(targetDir, 'roles');
192+
const indexTarget = join(targetDir, 'index.yaml');
193+
194+
// For claude-code and gemini, skills go directly in the skills dir
195+
// not nested under a subdirectory
196+
let sTarget, rTarget, iTarget;
197+
if (key === 'generic') {
198+
sTarget = join(targetDir, 'skills');
199+
rTarget = join(targetDir, 'roles');
200+
iTarget = join(targetDir, 'index.yaml');
201+
} else {
202+
// For tool-specific dirs, put content directly in the skills dir
203+
sTarget = targetDir;
204+
rTarget = join(dirname(targetDir), 'roles');
205+
iTarget = join(dirname(targetDir), 'security-index.yaml');
206+
}
207+
208+
const fileCount = copyRecursive(skillsSrc, sTarget);
209+
const roleCount = copyRecursive(rolesSrc, rTarget);
210+
mkdirSync(dirname(iTarget), { recursive: true });
211+
copyFileSync(indexSrc, iTarget);
212+
213+
console.log(` ${cfg.name}`);
214+
console.log(` ${fileCount} skill files → ${sTarget}`);
215+
console.log(` ${roleCount} role files → ${rTarget}`);
216+
console.log(` index.yaml → ${iTarget}`);
217+
console.log('');
218+
}
219+
220+
console.log(' Done! Try asking your AI agent:\n');
221+
console.log(' "Run a threat model on this project"');
222+
console.log(' "Review this code for security issues"');
223+
console.log(' "Triage CVE-2024-XXXX for our stack"');
224+
console.log('');
225+
}
226+
227+
// --- Main ---
228+
229+
const args = process.argv.slice(2);
230+
const command = args[0];
231+
232+
if (args.includes('--version') || args.includes('-v')) {
233+
console.log(VERSION);
234+
} else if (args.includes('--help') || args.includes('-h') || !command) {
235+
printUsage();
236+
} else if (command === 'init') {
237+
init(args.slice(1));
238+
} else if (command === 'list') {
239+
listSkills();
240+
} else {
241+
console.error(` Unknown command: ${command}`);
242+
printUsage();
243+
process.exit(1);
244+
}

package.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "@unitoneai/skills",
3+
"version": "1.0.0",
4+
"description": "45 security skills for AI coding agents — Claude Code, Gemini CLI, Cursor, Codex, and more",
5+
"license": "MIT",
6+
"type": "module",
7+
"bin": {
8+
"skills": "./bin/skills.mjs"
9+
},
10+
"files": [
11+
"bin/",
12+
"skills/",
13+
"roles/",
14+
"index.yaml",
15+
"README.md",
16+
"LICENSE"
17+
],
18+
"keywords": [
19+
"security",
20+
"claude-code",
21+
"gemini-cli",
22+
"cursor",
23+
"codex",
24+
"skills",
25+
"appsec",
26+
"devsecops",
27+
"threat-modeling",
28+
"vulnerability-management",
29+
"ai-security"
30+
],
31+
"repository": {
32+
"type": "git",
33+
"url": "https://github.com/UnitOneAI/SecuritySkills"
34+
},
35+
"homepage": "https://github.com/UnitOneAI/SecuritySkills#readme",
36+
"author": "UnitOne AI",
37+
"engines": {
38+
"node": ">=18"
39+
}
40+
}

0 commit comments

Comments
 (0)