Initial release v1.0.0

Open-source marketplace for AI Agent skills.

Features:
- Next.js 15 web app with i18n (en/fa)
- CLI tool for skill installation (npx skillhub)
- GitHub crawler/indexer with multi-strategy discovery
- Security scanning for all indexed skills
- Self-hostable with Docker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-12 06:08:51 +03:30
commit 97b427831a
227 changed files with 48411 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
import { describe, it, expect } from 'vitest';
import { validateSkill, isValidSkill, formatValidationSummary } from './validator.js';
import type { ParsedSkill } from './types.js';
// Helper function to create a mock parsed skill
function createMockSkill(overrides: Partial<ParsedSkill> = {}): ParsedSkill {
return {
metadata: {
name: 'test-skill',
description: 'A test skill',
version: '1.0.0',
license: 'MIT',
compatibility: {
platforms: ['claude', 'codex'],
},
...overrides.metadata,
},
content: `# Test Skill
This is a test skill with good content structure.
## Usage
Here's how to use this skill with an example:
\`\`\`bash
npx skillhub install test/test-skill
\`\`\`
`,
resources: {
scripts: [],
references: [],
...overrides.resources,
},
validation: {
isValid: true,
errors: [],
warnings: [],
...overrides.validation,
},
...overrides,
};
}
describe('validateSkill', () => {
it('should return valid for a well-formed skill', () => {
const skill = createMockSkill();
const result = validateSkill(skill);
expect(result.isValid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it('should detect empty content', () => {
const skill = createMockSkill({ content: '' });
const result = validateSkill(skill);
expect(result.isValid).toBe(false);
expect(result.errors.some(e => e.code === 'EMPTY_CONTENT')).toBe(true);
});
it('should warn about short content', () => {
const skill = createMockSkill({ content: 'Very short.' });
const result = validateSkill(skill);
expect(result.warnings.some(w => w.code === 'CONTENT_TOO_SHORT')).toBe(true);
});
it('should warn about placeholder content', () => {
const skill = createMockSkill({
content: `# Test Skill
TODO: Add more content here.
## Usage
FIXME: Write usage instructions.
`,
});
const result = validateSkill(skill);
expect(result.warnings.some(w => w.code === 'PLACEHOLDER_CONTENT')).toBe(true);
});
it('should warn about missing version', () => {
const skill = createMockSkill({
metadata: {
name: 'test-skill',
description: 'A test skill',
version: undefined,
},
});
const result = validateSkill(skill);
expect(result.warnings.some(w => w.code === 'NO_VERSION')).toBe(true);
});
it('should warn about missing license', () => {
const skill = createMockSkill({
metadata: {
name: 'test-skill',
description: 'A test skill',
license: undefined,
},
});
const result = validateSkill(skill);
expect(result.warnings.some(w => w.code === 'NO_LICENSE')).toBe(true);
});
it('should warn about missing platforms', () => {
const skill = createMockSkill({
metadata: {
name: 'test-skill',
description: 'A test skill',
compatibility: {},
},
});
const result = validateSkill(skill);
expect(result.warnings.some(w => w.code === 'NO_PLATFORMS')).toBe(true);
});
it('should warn about unusual script extensions', () => {
const skill = createMockSkill({
resources: {
scripts: [{ name: 'script.xyz', content: 'echo hello' }],
references: [],
},
});
const result = validateSkill(skill);
expect(result.warnings.some(w => w.code === 'UNUSUAL_SCRIPT_EXT')).toBe(true);
});
it('should warn about large reference files', () => {
const skill = createMockSkill({
resources: {
scripts: [],
references: [{ name: 'huge-file.md', content: 'x'.repeat(150000) }],
},
});
const result = validateSkill(skill);
expect(result.warnings.some(w => w.code === 'LARGE_REFERENCE')).toBe(true);
});
});
describe('isValidSkill', () => {
it('should return true for valid skills', () => {
const skill = createMockSkill();
expect(isValidSkill(skill)).toBe(true);
});
it('should return false for invalid skills', () => {
const skill = createMockSkill({
validation: {
isValid: false,
errors: [{ code: 'MISSING_NAME', message: 'Name is required' }],
warnings: [],
},
});
expect(isValidSkill(skill)).toBe(false);
});
});
describe('formatValidationSummary', () => {
it('should format valid skill summary', () => {
const result = {
isValid: true,
errors: [],
warnings: [],
};
const summary = formatValidationSummary(result);
expect(summary).toContain('Skill is valid');
});
it('should format errors and warnings', () => {
const result = {
isValid: false,
errors: [{ code: 'EMPTY_CONTENT', message: 'Content is empty' }],
warnings: [{ code: 'NO_LICENSE', message: 'No license', suggestion: 'Add MIT' }],
};
const summary = formatValidationSummary(result);
expect(summary).toContain('ERROR: Content is empty');
expect(summary).toContain('WARNING: No license');
expect(summary).toContain('Suggestion: Add MIT');
});
});