ma-agents 1.2.0 → 1.4.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/DEVELOPMENT.md +173 -0
- package/README.md +201 -309
- package/SKILLS_STRUCTURE.md +392 -0
- package/bin/cli.js +135 -28
- package/index.js +8 -6
- package/lib/agents.js +26 -8
- package/lib/installer.js +312 -45
- package/package.json +1 -1
- package/skills/code-review/SKILL.md +39 -0
- package/skills/commit-message/SKILL.md +75 -0
- package/skills/create-hardened-docker-skill/SKILL.md +0 -5
- package/skills/git-workflow-skill/SKILL.md +0 -5
- package/skills/js-ts-security-skill/SKILL.md +0 -4
- package/skills/skill-creator/SKILL.md +211 -0
- package/skills/skill-creator/claude-code.md +6 -8
- package/skills/skill-creator/generic.md +0 -5
- package/skills/test-generator/SKILL.md +61 -0
- package/skills/vercel-react-best-practices/SKILL.md +105 -0
- package/skills/verify-hardened-docker-skill/SKILL.md +0 -5
package/lib/installer.js
CHANGED
|
@@ -1,11 +1,72 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
+
const prompts = require('prompts');
|
|
4
5
|
const { getAgent, getAllAgents } = require('./agents');
|
|
5
6
|
|
|
7
|
+
const MANIFEST_FILE = '.ma-agents.json';
|
|
8
|
+
const MANIFEST_VERSION = '1.0.0';
|
|
9
|
+
|
|
10
|
+
function getPackageVersion() {
|
|
11
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
12
|
+
return pkg.version;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// --- Manifest functions ---
|
|
16
|
+
|
|
17
|
+
function readManifest(installPath) {
|
|
18
|
+
const manifestPath = path.join(installPath, MANIFEST_FILE);
|
|
19
|
+
if (!fs.existsSync(manifestPath)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function writeManifest(installPath, manifest) {
|
|
30
|
+
const manifestPath = path.join(installPath, MANIFEST_FILE);
|
|
31
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ensureManifest(installPath, agentId, scope) {
|
|
35
|
+
let manifest = readManifest(installPath);
|
|
36
|
+
if (!manifest) {
|
|
37
|
+
manifest = {
|
|
38
|
+
manifestVersion: MANIFEST_VERSION,
|
|
39
|
+
agent: agentId,
|
|
40
|
+
scope: scope,
|
|
41
|
+
skills: {}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return manifest;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getInstalledSkillInfo(installPath, skillId) {
|
|
48
|
+
const manifest = readManifest(installPath);
|
|
49
|
+
if (!manifest || !manifest.skills || !manifest.skills[skillId]) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return manifest.skills[skillId];
|
|
53
|
+
}
|
|
54
|
+
|
|
6
55
|
/**
|
|
7
|
-
*
|
|
56
|
+
* Compare two semver strings. Returns -1, 0, or 1.
|
|
8
57
|
*/
|
|
58
|
+
function compareSemver(a, b) {
|
|
59
|
+
const pa = (a || '0.0.0').split('.').map(Number);
|
|
60
|
+
const pb = (b || '0.0.0').split('.').map(Number);
|
|
61
|
+
for (let i = 0; i < 3; i++) {
|
|
62
|
+
if (pa[i] > pb[i]) return 1;
|
|
63
|
+
if (pa[i] < pb[i]) return -1;
|
|
64
|
+
}
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --- Skill listing ---
|
|
69
|
+
|
|
9
70
|
function listSkills() {
|
|
10
71
|
const skillsDir = path.join(__dirname, '..', 'skills');
|
|
11
72
|
|
|
@@ -32,17 +93,82 @@ function listSkills() {
|
|
|
32
93
|
}).filter(Boolean);
|
|
33
94
|
}
|
|
34
95
|
|
|
35
|
-
/**
|
|
36
|
-
* Get all supported agents
|
|
37
|
-
*/
|
|
38
96
|
function listAgents() {
|
|
39
97
|
return getAllAgents();
|
|
40
98
|
}
|
|
41
99
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
100
|
+
// --- Core install logic (no prompts) ---
|
|
101
|
+
|
|
102
|
+
async function performInstall(skillId, skill, agent, installPath) {
|
|
103
|
+
const skillSourceDir = path.join(__dirname, '..', 'skills', skillId);
|
|
104
|
+
let sourceFile = path.join(skillSourceDir, `${agent.template}${agent.fileExtension}`);
|
|
105
|
+
|
|
106
|
+
if (!fs.existsSync(sourceFile)) {
|
|
107
|
+
sourceFile = path.join(skillSourceDir, `generic${agent.fileExtension}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!fs.existsSync(sourceFile)) {
|
|
111
|
+
const skillMdPath = path.join(skillSourceDir, 'SKILL.md');
|
|
112
|
+
if (fs.existsSync(skillMdPath)) {
|
|
113
|
+
sourceFile = skillMdPath;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!fs.existsSync(sourceFile)) {
|
|
118
|
+
console.log(chalk.yellow(` Warning: No template found for ${agent.name}, skipping`));
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const skillDir = path.join(installPath, skillId);
|
|
123
|
+
await fs.ensureDir(skillDir);
|
|
124
|
+
|
|
125
|
+
let content = await fs.readFile(sourceFile, 'utf-8');
|
|
126
|
+
|
|
127
|
+
// Strip any existing YAML frontmatter from the source
|
|
128
|
+
const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
|
|
129
|
+
content = content.replace(frontmatterRegex, '');
|
|
130
|
+
|
|
131
|
+
// Inject YAML frontmatter from skill.json (single source of truth)
|
|
132
|
+
const frontmatter = [
|
|
133
|
+
'---',
|
|
134
|
+
`name: ${skill.name}`,
|
|
135
|
+
`description: ${skill.description}`,
|
|
136
|
+
'---',
|
|
137
|
+
''
|
|
138
|
+
].join('\n');
|
|
139
|
+
content = frontmatter + content;
|
|
140
|
+
|
|
141
|
+
const targetFile = path.join(skillDir, 'SKILL.md');
|
|
142
|
+
await fs.writeFile(targetFile, content, 'utf-8');
|
|
143
|
+
console.log(chalk.green(` + Installed to ${targetFile}`));
|
|
144
|
+
|
|
145
|
+
// Copy bundled resources
|
|
146
|
+
const resourceMap = agent.resourceMap || {};
|
|
147
|
+
const resourceDirs = ['scripts', 'references', 'assets', 'examples', 'hooks', 'docs', 'templates'];
|
|
148
|
+
for (const dir of resourceDirs) {
|
|
149
|
+
const resourceSource = path.join(skillSourceDir, dir);
|
|
150
|
+
if (fs.existsSync(resourceSource)) {
|
|
151
|
+
const targetDirName = resourceMap[dir] || dir;
|
|
152
|
+
const resourceTarget = path.join(skillDir, targetDirName);
|
|
153
|
+
await fs.copy(resourceSource, resourceTarget);
|
|
154
|
+
console.log(chalk.green(` + Copied ${dir}/ → ${targetDirName}/`));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Copy template.md if it exists
|
|
159
|
+
const templateSource = path.join(skillSourceDir, 'template.md');
|
|
160
|
+
if (fs.existsSync(templateSource)) {
|
|
161
|
+
await fs.copy(templateSource, path.join(skillDir, 'template.md'));
|
|
162
|
+
console.log(chalk.green(` + Copied template.md`));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// --- Install with upgrade detection ---
|
|
169
|
+
|
|
170
|
+
async function installSkill(skillId, agentIds, customPath = '', scope = 'project', options = {}) {
|
|
171
|
+
const { force = false } = options;
|
|
46
172
|
const skills = listSkills();
|
|
47
173
|
const skill = skills.find(s => s.id === skillId);
|
|
48
174
|
|
|
@@ -50,7 +176,7 @@ async function installSkill(skillId, agentIds, customPath = '') {
|
|
|
50
176
|
throw new Error(`Skill '${skillId}' not found. Run "list" to see available skills.`);
|
|
51
177
|
}
|
|
52
178
|
|
|
53
|
-
console.log(chalk.cyan(`\nInstalling skill: ${skill.name}`));
|
|
179
|
+
console.log(chalk.cyan(`\nInstalling skill: ${skill.name} v${skill.version}`));
|
|
54
180
|
|
|
55
181
|
for (const agentId of agentIds) {
|
|
56
182
|
const agent = getAgent(agentId);
|
|
@@ -60,61 +186,202 @@ async function installSkill(skillId, agentIds, customPath = '') {
|
|
|
60
186
|
continue;
|
|
61
187
|
}
|
|
62
188
|
|
|
63
|
-
console.log(chalk.gray(` Installing for ${agent.name}...`));
|
|
64
|
-
|
|
65
189
|
try {
|
|
66
|
-
|
|
67
|
-
const installPath = customPath || agent.getSkillsPath();
|
|
68
|
-
|
|
69
|
-
// Ensure the skills directory exists
|
|
190
|
+
const installPath = customPath || (scope === 'global' ? agent.getGlobalPath() : agent.getProjectPath());
|
|
70
191
|
await fs.ensureDir(installPath);
|
|
71
192
|
|
|
72
|
-
|
|
73
|
-
const skillSourceDir = path.join(__dirname, '..', 'skills', skillId);
|
|
74
|
-
let sourceFile = path.join(skillSourceDir, `${agent.template}${agent.fileExtension}`);
|
|
193
|
+
const installed = getInstalledSkillInfo(installPath, skillId);
|
|
75
194
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
195
|
+
if (installed && !force) {
|
|
196
|
+
const cmp = compareSemver(skill.version, installed.version);
|
|
197
|
+
|
|
198
|
+
let action;
|
|
199
|
+
if (cmp > 0) {
|
|
200
|
+
// Upgrade available
|
|
201
|
+
console.log(chalk.yellow(` ${skill.name} v${installed.version} → v${skill.version} update available for ${agent.name}`));
|
|
202
|
+
const { choice } = await prompts({
|
|
203
|
+
type: 'select',
|
|
204
|
+
name: 'choice',
|
|
205
|
+
message: 'What would you like to do?',
|
|
206
|
+
choices: [
|
|
207
|
+
{ title: 'Update (recommended)', value: 'update' },
|
|
208
|
+
{ title: 'Skip (keep current)', value: 'skip' },
|
|
209
|
+
{ title: 'Clean reinstall', value: 'reinstall' },
|
|
210
|
+
{ title: 'Remove (uninstall)', value: 'remove' }
|
|
211
|
+
]
|
|
212
|
+
});
|
|
213
|
+
action = choice;
|
|
214
|
+
} else if (cmp === 0) {
|
|
215
|
+
// Same version
|
|
216
|
+
console.log(chalk.gray(` ${skill.name} v${installed.version} already installed for ${agent.name}`));
|
|
217
|
+
const { choice } = await prompts({
|
|
218
|
+
type: 'select',
|
|
219
|
+
name: 'choice',
|
|
220
|
+
message: 'What would you like to do?',
|
|
221
|
+
choices: [
|
|
222
|
+
{ title: 'Skip (keep current)', value: 'skip' },
|
|
223
|
+
{ title: 'Clean reinstall', value: 'reinstall' },
|
|
224
|
+
{ title: 'Remove (uninstall)', value: 'remove' }
|
|
225
|
+
]
|
|
226
|
+
});
|
|
227
|
+
action = choice;
|
|
228
|
+
} else {
|
|
229
|
+
// Downgrade
|
|
230
|
+
console.log(chalk.yellow(` ${skill.name} v${installed.version} installed, package has v${skill.version} for ${agent.name}`));
|
|
231
|
+
const { choice } = await prompts({
|
|
232
|
+
type: 'select',
|
|
233
|
+
name: 'choice',
|
|
234
|
+
message: 'What would you like to do?',
|
|
235
|
+
choices: [
|
|
236
|
+
{ title: 'Skip (keep current)', value: 'skip' },
|
|
237
|
+
{ title: `Downgrade to v${skill.version}`, value: 'update' },
|
|
238
|
+
{ title: 'Remove (uninstall)', value: 'remove' }
|
|
239
|
+
]
|
|
240
|
+
});
|
|
241
|
+
action = choice;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!action || action === 'skip') {
|
|
245
|
+
console.log(chalk.gray(` Skipped`));
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (action === 'remove') {
|
|
250
|
+
await performUninstall(skillId, installPath);
|
|
251
|
+
const manifest = ensureManifest(installPath, agentId, scope);
|
|
252
|
+
delete manifest.skills[skillId];
|
|
253
|
+
writeManifest(installPath, manifest);
|
|
254
|
+
console.log(chalk.green(` - Removed ${skill.name} from ${agent.name}`));
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
80
257
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const skillMdPath = path.join(skillSourceDir, 'SKILL.md');
|
|
84
|
-
if (fs.existsSync(skillMdPath)) {
|
|
85
|
-
sourceFile = skillMdPath;
|
|
258
|
+
if (action === 'reinstall') {
|
|
259
|
+
await performUninstall(skillId, installPath);
|
|
86
260
|
}
|
|
261
|
+
|
|
262
|
+
// action === 'update' or 'reinstall' → proceed to install below
|
|
87
263
|
}
|
|
88
264
|
|
|
89
|
-
if (!
|
|
90
|
-
console.log(chalk.
|
|
91
|
-
|
|
265
|
+
if (!installed || force) {
|
|
266
|
+
console.log(chalk.gray(` Installing for ${agent.name}...`));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const success = await performInstall(skillId, skill, agent, installPath);
|
|
270
|
+
|
|
271
|
+
if (success) {
|
|
272
|
+
// Update manifest
|
|
273
|
+
const manifest = ensureManifest(installPath, agentId, scope);
|
|
274
|
+
const now = new Date().toISOString();
|
|
275
|
+
const existing = manifest.skills[skillId];
|
|
276
|
+
manifest.skills[skillId] = {
|
|
277
|
+
version: skill.version,
|
|
278
|
+
installedAt: existing ? existing.installedAt : now,
|
|
279
|
+
updatedAt: now,
|
|
280
|
+
installerVersion: getPackageVersion(),
|
|
281
|
+
agentVersion: agent.version
|
|
282
|
+
};
|
|
283
|
+
writeManifest(installPath, manifest);
|
|
92
284
|
}
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.log(chalk.red(` x Failed: ${error.message}`));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// --- Uninstall ---
|
|
292
|
+
|
|
293
|
+
async function performUninstall(skillId, installPath) {
|
|
294
|
+
const skillDir = path.join(installPath, skillId);
|
|
295
|
+
if (fs.existsSync(skillDir)) {
|
|
296
|
+
await fs.remove(skillDir);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function uninstallSkill(skillId, agentIds, customPath = '', scope = 'project') {
|
|
301
|
+
console.log(chalk.cyan(`\nUninstalling skill: ${skillId}`));
|
|
302
|
+
|
|
303
|
+
for (const agentId of agentIds) {
|
|
304
|
+
const agent = getAgent(agentId);
|
|
93
305
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
await
|
|
107
|
-
console.log(chalk.green(`
|
|
306
|
+
if (!agent) {
|
|
307
|
+
console.log(chalk.yellow(` Warning: Skipping unknown agent: ${agentId}`));
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const installPath = customPath || (scope === 'global' ? agent.getGlobalPath() : agent.getProjectPath());
|
|
313
|
+
const installed = getInstalledSkillInfo(installPath, skillId);
|
|
314
|
+
|
|
315
|
+
if (!installed) {
|
|
316
|
+
const skillDir = path.join(installPath, skillId);
|
|
317
|
+
if (fs.existsSync(skillDir)) {
|
|
318
|
+
await performUninstall(skillId, installPath);
|
|
319
|
+
console.log(chalk.green(` - Removed ${skillId} from ${agent.name} (legacy install)`));
|
|
320
|
+
} else {
|
|
321
|
+
console.log(chalk.gray(` ${skillId} is not installed for ${agent.name}`));
|
|
108
322
|
}
|
|
323
|
+
continue;
|
|
109
324
|
}
|
|
325
|
+
|
|
326
|
+
await performUninstall(skillId, installPath);
|
|
327
|
+
|
|
328
|
+
const manifest = ensureManifest(installPath, agentId, scope);
|
|
329
|
+
delete manifest.skills[skillId];
|
|
330
|
+
writeManifest(installPath, manifest);
|
|
331
|
+
|
|
332
|
+
console.log(chalk.green(` - Removed ${skillId} v${installed.version} from ${agent.name}`));
|
|
110
333
|
} catch (error) {
|
|
111
334
|
console.log(chalk.red(` x Failed: ${error.message}`));
|
|
112
335
|
}
|
|
113
336
|
}
|
|
114
337
|
}
|
|
115
338
|
|
|
339
|
+
// --- Status ---
|
|
340
|
+
|
|
341
|
+
function getStatus(agentIds, customPath = '', scope = 'project') {
|
|
342
|
+
const results = [];
|
|
343
|
+
|
|
344
|
+
const targetAgents = agentIds && agentIds.length > 0
|
|
345
|
+
? agentIds.map(id => getAgent(id)).filter(Boolean)
|
|
346
|
+
: getAllAgents();
|
|
347
|
+
|
|
348
|
+
for (const agent of targetAgents) {
|
|
349
|
+
const projectPath = agent.getProjectPath();
|
|
350
|
+
const globalPath = agent.getGlobalPath();
|
|
351
|
+
|
|
352
|
+
// Check both scopes unless custom path is specified
|
|
353
|
+
const pathsToCheck = customPath
|
|
354
|
+
? [{ path: customPath, scope: 'custom' }]
|
|
355
|
+
: [
|
|
356
|
+
{ path: projectPath, scope: 'project' },
|
|
357
|
+
{ path: globalPath, scope: 'global' }
|
|
358
|
+
];
|
|
359
|
+
|
|
360
|
+
for (const { path: checkPath, scope: checkScope } of pathsToCheck) {
|
|
361
|
+
const manifest = readManifest(checkPath);
|
|
362
|
+
if (!manifest || !manifest.skills || Object.keys(manifest.skills).length === 0) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
results.push({
|
|
367
|
+
agent: agent,
|
|
368
|
+
installPath: checkPath,
|
|
369
|
+
scope: checkScope,
|
|
370
|
+
skills: manifest.skills
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return results;
|
|
376
|
+
}
|
|
377
|
+
|
|
116
378
|
module.exports = {
|
|
117
379
|
listSkills,
|
|
118
380
|
listAgents,
|
|
119
|
-
installSkill
|
|
381
|
+
installSkill,
|
|
382
|
+
uninstallSkill,
|
|
383
|
+
getStatus,
|
|
384
|
+
readManifest,
|
|
385
|
+
getInstalledSkillInfo,
|
|
386
|
+
compareSemver
|
|
120
387
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Code Review
|
|
2
|
+
|
|
3
|
+
Perform comprehensive code reviews following industry best practices.
|
|
4
|
+
|
|
5
|
+
## What to Review
|
|
6
|
+
|
|
7
|
+
1. **Code Quality**: Readability, naming conventions, structure
|
|
8
|
+
2. **Best Practices**: Language-specific patterns, error handling, performance
|
|
9
|
+
3. **Security**: Common vulnerabilities (SQL injection, XSS, CSRF, etc.)
|
|
10
|
+
4. **Testing**: Coverage, edge cases, test quality
|
|
11
|
+
5. **Documentation**: Comments, API docs, clarity
|
|
12
|
+
|
|
13
|
+
## Review Process
|
|
14
|
+
|
|
15
|
+
- Analyze code for bugs and logical errors
|
|
16
|
+
- Check adherence to coding standards
|
|
17
|
+
- Identify security vulnerabilities
|
|
18
|
+
- Suggest refactoring opportunities
|
|
19
|
+
- Assess test coverage and documentation
|
|
20
|
+
|
|
21
|
+
## Output Format
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
## Code Review Summary
|
|
25
|
+
|
|
26
|
+
### Strengths
|
|
27
|
+
- [Positive aspects]
|
|
28
|
+
|
|
29
|
+
### Issues Found
|
|
30
|
+
- **[High/Medium/Low]** [Issue description]
|
|
31
|
+
- Location: [file:line]
|
|
32
|
+
- Fix: [recommendation]
|
|
33
|
+
|
|
34
|
+
### Suggestions
|
|
35
|
+
- [Improvements]
|
|
36
|
+
|
|
37
|
+
### Overall Assessment
|
|
38
|
+
[Summary and rating]
|
|
39
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Commit Message Generator
|
|
2
|
+
|
|
3
|
+
Generate meaningful commit messages following Conventional Commits specification.
|
|
4
|
+
|
|
5
|
+
## Format
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
<type>(<scope>): <subject>
|
|
9
|
+
|
|
10
|
+
<body>
|
|
11
|
+
|
|
12
|
+
<footer>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Types
|
|
16
|
+
|
|
17
|
+
- `feat`: New feature
|
|
18
|
+
- `fix`: Bug fix
|
|
19
|
+
- `docs`: Documentation changes
|
|
20
|
+
- `style`: Code style/formatting
|
|
21
|
+
- `refactor`: Code refactoring
|
|
22
|
+
- `test`: Adding/updating tests
|
|
23
|
+
- `chore`: Maintenance tasks
|
|
24
|
+
- `perf`: Performance improvements
|
|
25
|
+
- `ci`: CI/CD changes
|
|
26
|
+
- `build`: Build system changes
|
|
27
|
+
- `revert`: Revert previous commit
|
|
28
|
+
|
|
29
|
+
## Guidelines
|
|
30
|
+
|
|
31
|
+
1. **Subject line** (max 50 chars):
|
|
32
|
+
- Use imperative mood ("Add" not "Added")
|
|
33
|
+
- Don't capitalize first letter
|
|
34
|
+
- No period at the end
|
|
35
|
+
|
|
36
|
+
2. **Body** (optional):
|
|
37
|
+
- Explain what and why, not how
|
|
38
|
+
- Wrap at 72 characters
|
|
39
|
+
|
|
40
|
+
3. **Footer** (optional):
|
|
41
|
+
- Breaking changes: `BREAKING CHANGE: description`
|
|
42
|
+
- Issue references: `Fixes #123`
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
feat(auth): add JWT token refresh mechanism
|
|
48
|
+
|
|
49
|
+
Implement automatic token refresh to improve user experience
|
|
50
|
+
and reduce re-authentication prompts.
|
|
51
|
+
|
|
52
|
+
Fixes #456
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
fix(api): resolve memory leak in user service
|
|
57
|
+
|
|
58
|
+
The user cache was not being cleared properly, causing
|
|
59
|
+
memory to grow over time.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
docs: update installation instructions
|
|
64
|
+
|
|
65
|
+
Add steps for Windows users and clarify dependency requirements.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Process
|
|
69
|
+
|
|
70
|
+
1. Analyze the code changes
|
|
71
|
+
2. Determine the type of change
|
|
72
|
+
3. Identify the scope (component/module affected)
|
|
73
|
+
4. Write clear, concise subject
|
|
74
|
+
5. Add body if changes need explanation
|
|
75
|
+
6. Add footer for breaking changes or issue refs
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: create-hardened-docker
|
|
3
|
-
description: Creates production-ready hardened Docker configurations (Dockerfile, docker-compose.yml, nginx.conf, .dockerignore) following CIS Docker Benchmark, OWASP, and NIST SP 800-190 standards. Includes multi-stage builds, non-root execution, read-only filesystems, capability dropping, and comprehensive security hardening.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# Create Hardened Docker
|
|
7
2
|
|
|
8
3
|
## Overview
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: git-workflow-skill
|
|
3
|
-
description: Mandatory feature branch workflow using git worktrees for parallel multi-agent development. Use this skill BEFORE making ANY change to files in a Git repository that are not ignored by .gitignore. Enforces isolated worktrees per feature, conventional commits, and PR-based merging. Supports multiple AI agents working simultaneously on different features without conflicts.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# Git Workflow (Worktree-Based)
|
|
7
2
|
|
|
8
3
|
Multi-agent parallel development using git worktrees. Each agent gets an isolated working directory — no branch switching, no conflicts between agents.
|