@skillsmith/mcp-server 0.1.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/dist/.tsbuildinfo +1 -0
- package/dist/src/__tests__/get-skill.test.d.ts +6 -0
- package/dist/src/__tests__/get-skill.test.d.ts.map +1 -0
- package/dist/src/__tests__/get-skill.test.js +88 -0
- package/dist/src/__tests__/get-skill.test.js.map +1 -0
- package/dist/src/__tests__/middleware/errorFormatter.test.d.ts +7 -0
- package/dist/src/__tests__/middleware/errorFormatter.test.d.ts.map +1 -0
- package/dist/src/__tests__/middleware/errorFormatter.test.js +304 -0
- package/dist/src/__tests__/middleware/errorFormatter.test.js.map +1 -0
- package/dist/src/__tests__/middleware/license.test.d.ts +7 -0
- package/dist/src/__tests__/middleware/license.test.d.ts.map +1 -0
- package/dist/src/__tests__/middleware/license.test.js +500 -0
- package/dist/src/__tests__/middleware/license.test.js.map +1 -0
- package/dist/src/__tests__/search.test.d.ts +6 -0
- package/dist/src/__tests__/search.test.d.ts.map +1 -0
- package/dist/src/__tests__/search.test.js +86 -0
- package/dist/src/__tests__/search.test.js.map +1 -0
- package/dist/src/__tests__/test-utils.d.ts +19 -0
- package/dist/src/__tests__/test-utils.d.ts.map +1 -0
- package/dist/src/__tests__/test-utils.js +87 -0
- package/dist/src/__tests__/test-utils.js.map +1 -0
- package/dist/src/context/index.d.ts +19 -0
- package/dist/src/context/index.d.ts.map +1 -0
- package/dist/src/context/index.js +25 -0
- package/dist/src/context/index.js.map +1 -0
- package/dist/src/context/project-detector.d.ts +145 -0
- package/dist/src/context/project-detector.d.ts.map +1 -0
- package/dist/src/context/project-detector.js +321 -0
- package/dist/src/context/project-detector.js.map +1 -0
- package/dist/src/context.d.ts +100 -0
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +157 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/core-shim.d.ts +7 -0
- package/dist/src/core-shim.d.ts.map +1 -0
- package/dist/src/core-shim.js +9 -0
- package/dist/src/core-shim.js.map +1 -0
- package/dist/src/health/healthCheck.d.ts +88 -0
- package/dist/src/health/healthCheck.d.ts.map +1 -0
- package/dist/src/health/healthCheck.js +117 -0
- package/dist/src/health/healthCheck.js.map +1 -0
- package/dist/src/health/index.d.ts +21 -0
- package/dist/src/health/index.d.ts.map +1 -0
- package/dist/src/health/index.js +21 -0
- package/dist/src/health/index.js.map +1 -0
- package/dist/src/health/readinessCheck.d.ts +139 -0
- package/dist/src/health/readinessCheck.d.ts.map +1 -0
- package/dist/src/health/readinessCheck.js +266 -0
- package/dist/src/health/readinessCheck.js.map +1 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +178 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/index.test.d.ts +2 -0
- package/dist/src/index.test.d.ts.map +1 -0
- package/dist/src/index.test.js +43 -0
- package/dist/src/index.test.js.map +1 -0
- package/dist/src/logger.d.ts +26 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +179 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/middleware/__tests__/csp.test.d.ts +2 -0
- package/dist/src/middleware/__tests__/csp.test.d.ts.map +1 -0
- package/dist/src/middleware/__tests__/csp.test.js +389 -0
- package/dist/src/middleware/__tests__/csp.test.js.map +1 -0
- package/dist/src/middleware/csp.d.ts +87 -0
- package/dist/src/middleware/csp.d.ts.map +1 -0
- package/dist/src/middleware/csp.js +273 -0
- package/dist/src/middleware/csp.js.map +1 -0
- package/dist/src/middleware/degradation.d.ts +99 -0
- package/dist/src/middleware/degradation.d.ts.map +1 -0
- package/dist/src/middleware/degradation.js +315 -0
- package/dist/src/middleware/degradation.js.map +1 -0
- package/dist/src/middleware/errorFormatter.d.ts +119 -0
- package/dist/src/middleware/errorFormatter.d.ts.map +1 -0
- package/dist/src/middleware/errorFormatter.js +294 -0
- package/dist/src/middleware/errorFormatter.js.map +1 -0
- package/dist/src/middleware/index.d.ts +10 -0
- package/dist/src/middleware/index.d.ts.map +1 -0
- package/dist/src/middleware/index.js +14 -0
- package/dist/src/middleware/index.js.map +1 -0
- package/dist/src/middleware/license.d.ts +161 -0
- package/dist/src/middleware/license.d.ts.map +1 -0
- package/dist/src/middleware/license.js +281 -0
- package/dist/src/middleware/license.js.map +1 -0
- package/dist/src/middleware/toolFeatureMapping.d.ts +36 -0
- package/dist/src/middleware/toolFeatureMapping.d.ts.map +1 -0
- package/dist/src/middleware/toolFeatureMapping.js +90 -0
- package/dist/src/middleware/toolFeatureMapping.js.map +1 -0
- package/dist/src/onboarding/first-run.d.ts +64 -0
- package/dist/src/onboarding/first-run.d.ts.map +1 -0
- package/dist/src/onboarding/first-run.js +77 -0
- package/dist/src/onboarding/first-run.js.map +1 -0
- package/dist/src/onboarding/index.d.ts +7 -0
- package/dist/src/onboarding/index.d.ts.map +1 -0
- package/dist/src/onboarding/index.js +7 -0
- package/dist/src/onboarding/index.js.map +1 -0
- package/dist/src/suggestions/index.d.ts +21 -0
- package/dist/src/suggestions/index.d.ts.map +1 -0
- package/dist/src/suggestions/index.js +20 -0
- package/dist/src/suggestions/index.js.map +1 -0
- package/dist/src/suggestions/suggestion-engine.d.ts +185 -0
- package/dist/src/suggestions/suggestion-engine.d.ts.map +1 -0
- package/dist/src/suggestions/suggestion-engine.js +352 -0
- package/dist/src/suggestions/suggestion-engine.js.map +1 -0
- package/dist/src/suggestions/types.d.ts +88 -0
- package/dist/src/suggestions/types.d.ts.map +1 -0
- package/dist/src/suggestions/types.js +21 -0
- package/dist/src/suggestions/types.js.map +1 -0
- package/dist/src/tools/analyze.d.ts +151 -0
- package/dist/src/tools/analyze.d.ts.map +1 -0
- package/dist/src/tools/analyze.js +205 -0
- package/dist/src/tools/analyze.js.map +1 -0
- package/dist/src/tools/compare.d.ts +149 -0
- package/dist/src/tools/compare.d.ts.map +1 -0
- package/dist/src/tools/compare.js +464 -0
- package/dist/src/tools/compare.js.map +1 -0
- package/dist/src/tools/get-skill.d.ts +116 -0
- package/dist/src/tools/get-skill.d.ts.map +1 -0
- package/dist/src/tools/get-skill.js +224 -0
- package/dist/src/tools/get-skill.js.map +1 -0
- package/dist/src/tools/index.d.ts +20 -0
- package/dist/src/tools/index.d.ts.map +1 -0
- package/dist/src/tools/index.js +20 -0
- package/dist/src/tools/index.js.map +1 -0
- package/dist/src/tools/install.d.ts +122 -0
- package/dist/src/tools/install.d.ts.map +1 -0
- package/dist/src/tools/install.js +314 -0
- package/dist/src/tools/install.js.map +1 -0
- package/dist/src/tools/recommend.d.ts +171 -0
- package/dist/src/tools/recommend.d.ts.map +1 -0
- package/dist/src/tools/recommend.js +325 -0
- package/dist/src/tools/recommend.js.map +1 -0
- package/dist/src/tools/search.d.ts +121 -0
- package/dist/src/tools/search.d.ts.map +1 -0
- package/dist/src/tools/search.js +249 -0
- package/dist/src/tools/search.js.map +1 -0
- package/dist/src/tools/suggest.d.ts +181 -0
- package/dist/src/tools/suggest.d.ts.map +1 -0
- package/dist/src/tools/suggest.js +342 -0
- package/dist/src/tools/suggest.js.map +1 -0
- package/dist/src/tools/uninstall.d.ts +123 -0
- package/dist/src/tools/uninstall.d.ts.map +1 -0
- package/dist/src/tools/uninstall.js +250 -0
- package/dist/src/tools/uninstall.js.map +1 -0
- package/dist/src/tools/validate.d.ts +122 -0
- package/dist/src/tools/validate.d.ts.map +1 -0
- package/dist/src/tools/validate.js +497 -0
- package/dist/src/tools/validate.js.map +1 -0
- package/dist/src/utils/installed-skills.d.ts +101 -0
- package/dist/src/utils/installed-skills.d.ts.map +1 -0
- package/dist/src/utils/installed-skills.js +220 -0
- package/dist/src/utils/installed-skills.js.map +1 -0
- package/dist/src/utils/validation.d.ts +76 -0
- package/dist/src/utils/validation.d.ts.map +1 -0
- package/dist/src/utils/validation.js +153 -0
- package/dist/src/utils/validation.js.map +1 -0
- package/dist/src/webhooks/index.d.ts +8 -0
- package/dist/src/webhooks/index.d.ts.map +1 -0
- package/dist/src/webhooks/index.js +9 -0
- package/dist/src/webhooks/index.js.map +1 -0
- package/dist/src/webhooks/webhook-endpoint.d.ts +149 -0
- package/dist/src/webhooks/webhook-endpoint.d.ts.map +1 -0
- package/dist/src/webhooks/webhook-endpoint.js +339 -0
- package/dist/src/webhooks/webhook-endpoint.js.map +1 -0
- package/dist/tests/compare.test.d.ts +6 -0
- package/dist/tests/compare.test.d.ts.map +1 -0
- package/dist/tests/compare.test.js +225 -0
- package/dist/tests/compare.test.js.map +1 -0
- package/dist/tests/context/project-detector.test.d.ts +6 -0
- package/dist/tests/context/project-detector.test.d.ts.map +1 -0
- package/dist/tests/context/project-detector.test.js +719 -0
- package/dist/tests/context/project-detector.test.js.map +1 -0
- package/dist/tests/e2e/compare.e2e.test.d.ts +10 -0
- package/dist/tests/e2e/compare.e2e.test.d.ts.map +1 -0
- package/dist/tests/e2e/compare.e2e.test.js +286 -0
- package/dist/tests/e2e/compare.e2e.test.js.map +1 -0
- package/dist/tests/e2e/install-flow.e2e.test.d.ts +10 -0
- package/dist/tests/e2e/install-flow.e2e.test.d.ts.map +1 -0
- package/dist/tests/e2e/install-flow.e2e.test.js +209 -0
- package/dist/tests/e2e/install-flow.e2e.test.js.map +1 -0
- package/dist/tests/e2e/recommend.e2e.test.d.ts +12 -0
- package/dist/tests/e2e/recommend.e2e.test.d.ts.map +1 -0
- package/dist/tests/e2e/recommend.e2e.test.js +347 -0
- package/dist/tests/e2e/recommend.e2e.test.js.map +1 -0
- package/dist/tests/e2e/skill-flow.e2e.test.d.ts +10 -0
- package/dist/tests/e2e/skill-flow.e2e.test.d.ts.map +1 -0
- package/dist/tests/e2e/skill-flow.e2e.test.js +280 -0
- package/dist/tests/e2e/skill-flow.e2e.test.js.map +1 -0
- package/dist/tests/e2e/suggest.e2e.test.d.ts +13 -0
- package/dist/tests/e2e/suggest.e2e.test.d.ts.map +1 -0
- package/dist/tests/e2e/suggest.e2e.test.js +347 -0
- package/dist/tests/e2e/suggest.e2e.test.js.map +1 -0
- package/dist/tests/e2e/utils/baseline-collector.d.ts +107 -0
- package/dist/tests/e2e/utils/baseline-collector.d.ts.map +1 -0
- package/dist/tests/e2e/utils/baseline-collector.js +211 -0
- package/dist/tests/e2e/utils/baseline-collector.js.map +1 -0
- package/dist/tests/e2e/utils/hardcoded-detector.d.ts +46 -0
- package/dist/tests/e2e/utils/hardcoded-detector.d.ts.map +1 -0
- package/dist/tests/e2e/utils/hardcoded-detector.js +255 -0
- package/dist/tests/e2e/utils/hardcoded-detector.js.map +1 -0
- package/dist/tests/e2e/utils/index.d.ts +7 -0
- package/dist/tests/e2e/utils/index.d.ts.map +1 -0
- package/dist/tests/e2e/utils/index.js +7 -0
- package/dist/tests/e2e/utils/index.js.map +1 -0
- package/dist/tests/e2e/utils/linear-reporter.d.ts +60 -0
- package/dist/tests/e2e/utils/linear-reporter.d.ts.map +1 -0
- package/dist/tests/e2e/utils/linear-reporter.js +232 -0
- package/dist/tests/e2e/utils/linear-reporter.js.map +1 -0
- package/dist/tests/health.test.d.ts +9 -0
- package/dist/tests/health.test.d.ts.map +1 -0
- package/dist/tests/health.test.js +308 -0
- package/dist/tests/health.test.js.map +1 -0
- package/dist/tests/integration/analyze.integration.test.d.ts +2 -0
- package/dist/tests/integration/analyze.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/analyze.integration.test.js +244 -0
- package/dist/tests/integration/analyze.integration.test.js.map +1 -0
- package/dist/tests/integration/compare.integration.test.d.ts +2 -0
- package/dist/tests/integration/compare.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/compare.integration.test.js +120 -0
- package/dist/tests/integration/compare.integration.test.js.map +1 -0
- package/dist/tests/integration/fixtures/test-skills.d.ts +62 -0
- package/dist/tests/integration/fixtures/test-skills.d.ts.map +1 -0
- package/dist/tests/integration/fixtures/test-skills.js +644 -0
- package/dist/tests/integration/fixtures/test-skills.js.map +1 -0
- package/dist/tests/integration/get-skill.integration.test.d.ts +6 -0
- package/dist/tests/integration/get-skill.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/get-skill.integration.test.js +203 -0
- package/dist/tests/integration/get-skill.integration.test.js.map +1 -0
- package/dist/tests/integration/github-api.integration.test.d.ts +14 -0
- package/dist/tests/integration/github-api.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/github-api.integration.test.js +190 -0
- package/dist/tests/integration/github-api.integration.test.js.map +1 -0
- package/dist/tests/integration/install.integration.test.d.ts +6 -0
- package/dist/tests/integration/install.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/install.integration.test.js +282 -0
- package/dist/tests/integration/install.integration.test.js.map +1 -0
- package/dist/tests/integration/recommend.integration.test.d.ts +2 -0
- package/dist/tests/integration/recommend.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/recommend.integration.test.js +215 -0
- package/dist/tests/integration/recommend.integration.test.js.map +1 -0
- package/dist/tests/integration/search.integration.test.d.ts +6 -0
- package/dist/tests/integration/search.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/search.integration.test.js +229 -0
- package/dist/tests/integration/search.integration.test.js.map +1 -0
- package/dist/tests/integration/setup.d.ts +71 -0
- package/dist/tests/integration/setup.d.ts.map +1 -0
- package/dist/tests/integration/setup.js +124 -0
- package/dist/tests/integration/setup.js.map +1 -0
- package/dist/tests/integration/uninstall.integration.test.d.ts +6 -0
- package/dist/tests/integration/uninstall.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/uninstall.integration.test.js +296 -0
- package/dist/tests/integration/uninstall.integration.test.js.map +1 -0
- package/dist/tests/integration/validate.integration.test.d.ts +2 -0
- package/dist/tests/integration/validate.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/validate.integration.test.js +181 -0
- package/dist/tests/integration/validate.integration.test.js.map +1 -0
- package/dist/tests/onboarding/first-run.test.d.ts +7 -0
- package/dist/tests/onboarding/first-run.test.d.ts.map +1 -0
- package/dist/tests/onboarding/first-run.test.js +258 -0
- package/dist/tests/onboarding/first-run.test.js.map +1 -0
- package/dist/tests/performance/search-performance.test.d.ts +10 -0
- package/dist/tests/performance/search-performance.test.d.ts.map +1 -0
- package/dist/tests/performance/search-performance.test.js +218 -0
- package/dist/tests/performance/search-performance.test.js.map +1 -0
- package/dist/tests/recommend.test.d.ts +6 -0
- package/dist/tests/recommend.test.d.ts.map +1 -0
- package/dist/tests/recommend.test.js +208 -0
- package/dist/tests/recommend.test.js.map +1 -0
- package/dist/tests/suggestions/suggestion-engine.test.d.ts +6 -0
- package/dist/tests/suggestions/suggestion-engine.test.d.ts.map +1 -0
- package/dist/tests/suggestions/suggestion-engine.test.js +448 -0
- package/dist/tests/suggestions/suggestion-engine.test.js.map +1 -0
- package/dist/tests/test-utils.d.ts +74 -0
- package/dist/tests/test-utils.d.ts.map +1 -0
- package/dist/tests/test-utils.js +98 -0
- package/dist/tests/test-utils.js.map +1 -0
- package/dist/tests/tools.test.d.ts +5 -0
- package/dist/tests/tools.test.d.ts.map +1 -0
- package/dist/tests/tools.test.js +138 -0
- package/dist/tests/tools.test.js.map +1 -0
- package/dist/tests/unit/installed-skills.test.d.ts +6 -0
- package/dist/tests/unit/installed-skills.test.d.ts.map +1 -0
- package/dist/tests/unit/installed-skills.test.js +285 -0
- package/dist/tests/unit/installed-skills.test.js.map +1 -0
- package/dist/tests/unit/logger.test.d.ts +6 -0
- package/dist/tests/unit/logger.test.d.ts.map +1 -0
- package/dist/tests/unit/logger.test.js +281 -0
- package/dist/tests/unit/logger.test.js.map +1 -0
- package/dist/tests/validate.test.d.ts +5 -0
- package/dist/tests/validate.test.d.ts.map +1 -0
- package/dist/tests/validate.test.js +303 -0
- package/dist/tests/validate.test.js.map +1 -0
- package/dist/tests/webhooks/proxy-trust.security.test.d.ts +8 -0
- package/dist/tests/webhooks/proxy-trust.security.test.d.ts.map +1 -0
- package/dist/tests/webhooks/proxy-trust.security.test.js +145 -0
- package/dist/tests/webhooks/proxy-trust.security.test.js.map +1 -0
- package/dist/tests/webhooks/rate-limiter.security.test.d.ts +8 -0
- package/dist/tests/webhooks/rate-limiter.security.test.d.ts.map +1 -0
- package/dist/tests/webhooks/rate-limiter.security.test.js +122 -0
- package/dist/tests/webhooks/rate-limiter.security.test.js.map +1 -0
- package/dist/vitest.config.d.ts +6 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/vitest.config.js +13 -0
- package/dist/vitest.config.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E Test: MCP skill_recommend tool
|
|
3
|
+
*
|
|
4
|
+
* Tests the recommend tool against real skill databases
|
|
5
|
+
* in a clean Codespace environment.
|
|
6
|
+
*
|
|
7
|
+
* FOCUS: Detect hardcoded values that caused SMI-902/904 issues
|
|
8
|
+
*
|
|
9
|
+
* User Journey: Get skill recommendations based on context
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
12
|
+
import { existsSync, rmSync, mkdirSync, writeFileSync } from 'fs';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { tmpdir, homedir } from 'os';
|
|
15
|
+
import { createDatabase, initializeSchema, SkillRepository, } from '@skillsmith/core';
|
|
16
|
+
import { createToolContext } from '../../src/context.js';
|
|
17
|
+
import { executeRecommend } from '../../src/tools/recommend.js';
|
|
18
|
+
import { scanForHardcoded } from './utils/hardcoded-detector.js';
|
|
19
|
+
import { recordTiming, measureAsync } from './utils/baseline-collector.js';
|
|
20
|
+
// Test configuration
|
|
21
|
+
const TEST_DIR = join(tmpdir(), 'skillsmith-e2e-recommend');
|
|
22
|
+
const TEST_DB_PATH = join(TEST_DIR, 'recommend-test.db');
|
|
23
|
+
const TEST_SKILLS_DIR = join(TEST_DIR, '.claude', 'skills');
|
|
24
|
+
// Diverse seed data for testing recommendations
|
|
25
|
+
const SEED_SKILLS = [
|
|
26
|
+
{
|
|
27
|
+
id: 'anthropic/commit',
|
|
28
|
+
name: 'commit',
|
|
29
|
+
description: 'Generate semantic commit messages following conventional commits',
|
|
30
|
+
author: 'anthropic',
|
|
31
|
+
repoUrl: 'https://github.com/anthropics/claude-code/tree/main/skills/commit',
|
|
32
|
+
qualityScore: 0.95,
|
|
33
|
+
trustTier: 'verified',
|
|
34
|
+
tags: ['development', 'git', 'commit', 'conventional-commits'],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'community/jest-helper',
|
|
38
|
+
name: 'jest-helper',
|
|
39
|
+
description: 'Generate Jest test cases for React components',
|
|
40
|
+
author: 'community',
|
|
41
|
+
repoUrl: 'https://github.com/skillsmith-community/jest-helper',
|
|
42
|
+
qualityScore: 0.87,
|
|
43
|
+
trustTier: 'community',
|
|
44
|
+
tags: ['testing', 'jest', 'react', 'unit-tests'],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'community/vitest-helper',
|
|
48
|
+
name: 'vitest-helper',
|
|
49
|
+
description: 'Generate Vitest test cases with modern ESM support',
|
|
50
|
+
author: 'community',
|
|
51
|
+
repoUrl: 'https://github.com/skillsmith-community/vitest-helper',
|
|
52
|
+
qualityScore: 0.85,
|
|
53
|
+
trustTier: 'community',
|
|
54
|
+
tags: ['testing', 'vitest', 'esm', 'typescript'],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'community/docker-compose',
|
|
58
|
+
name: 'docker-compose',
|
|
59
|
+
description: 'Generate and manage Docker Compose configurations',
|
|
60
|
+
author: 'community',
|
|
61
|
+
repoUrl: 'https://github.com/skillsmith-community/docker-compose',
|
|
62
|
+
qualityScore: 0.84,
|
|
63
|
+
trustTier: 'community',
|
|
64
|
+
tags: ['devops', 'docker', 'containers'],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'community/eslint-config',
|
|
68
|
+
name: 'eslint-config',
|
|
69
|
+
description: 'Generate and configure ESLint rules for JavaScript projects',
|
|
70
|
+
author: 'community',
|
|
71
|
+
repoUrl: 'https://github.com/skillsmith-community/eslint-config',
|
|
72
|
+
qualityScore: 0.82,
|
|
73
|
+
trustTier: 'community',
|
|
74
|
+
tags: ['linting', 'eslint', 'code-quality'],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'experimental/ai-debug',
|
|
78
|
+
name: 'ai-debug',
|
|
79
|
+
description: 'AI-powered debugging assistant',
|
|
80
|
+
author: 'experimental',
|
|
81
|
+
repoUrl: 'https://github.com/skillsmith-community/ai-debug',
|
|
82
|
+
qualityScore: 0.65,
|
|
83
|
+
trustTier: 'experimental',
|
|
84
|
+
tags: ['debugging', 'ai', 'experimental'],
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
/**
|
|
88
|
+
* Create mock installed skill for auto-detection tests
|
|
89
|
+
*/
|
|
90
|
+
function createMockInstalledSkill(skillName) {
|
|
91
|
+
const skillDir = join(TEST_SKILLS_DIR, skillName);
|
|
92
|
+
mkdirSync(skillDir, { recursive: true });
|
|
93
|
+
writeFileSync(join(skillDir, 'SKILL.md'), `---
|
|
94
|
+
name: ${skillName}
|
|
95
|
+
version: 1.0.0
|
|
96
|
+
description: Mock skill for testing
|
|
97
|
+
---
|
|
98
|
+
# ${skillName}
|
|
99
|
+
Mock skill content.
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Scan response for hardcoded values
|
|
104
|
+
*/
|
|
105
|
+
function scanResponseForHardcoded(response, command) {
|
|
106
|
+
const responseStr = JSON.stringify(response, null, 2);
|
|
107
|
+
return scanForHardcoded(responseStr, command, 'database', 'recommend response');
|
|
108
|
+
}
|
|
109
|
+
describe('E2E: skill_recommend tool', () => {
|
|
110
|
+
let db;
|
|
111
|
+
let context;
|
|
112
|
+
beforeAll(() => {
|
|
113
|
+
// Create test environment
|
|
114
|
+
if (existsSync(TEST_DIR)) {
|
|
115
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
116
|
+
}
|
|
117
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
118
|
+
mkdirSync(TEST_SKILLS_DIR, { recursive: true });
|
|
119
|
+
// Initialize database with seed data
|
|
120
|
+
db = createDatabase(TEST_DB_PATH);
|
|
121
|
+
initializeSchema(db);
|
|
122
|
+
const skillRepo = new SkillRepository(db);
|
|
123
|
+
for (const skill of SEED_SKILLS) {
|
|
124
|
+
skillRepo.create(skill);
|
|
125
|
+
}
|
|
126
|
+
// Create context pointing to test database
|
|
127
|
+
context = createToolContext({ dbPath: TEST_DB_PATH });
|
|
128
|
+
});
|
|
129
|
+
afterAll(() => {
|
|
130
|
+
db?.close();
|
|
131
|
+
if (existsSync(TEST_DIR)) {
|
|
132
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
describe('Basic Recommendations', () => {
|
|
136
|
+
it('should return recommendations without installed skills', async () => {
|
|
137
|
+
const input = {
|
|
138
|
+
installed_skills: [],
|
|
139
|
+
limit: 5,
|
|
140
|
+
};
|
|
141
|
+
const { result } = await measureAsync('recommend:empty', 'skill_recommend (no installed)', () => executeRecommend(input, context));
|
|
142
|
+
expect(result.recommendations.length).toBeGreaterThan(0);
|
|
143
|
+
expect(result.timing.totalMs).toBeGreaterThanOrEqual(0);
|
|
144
|
+
// Check for hardcoded values in response
|
|
145
|
+
const issues = scanResponseForHardcoded(result, 'skill_recommend');
|
|
146
|
+
expect(issues.filter((i) => i.severity === 'error')).toHaveLength(0);
|
|
147
|
+
});
|
|
148
|
+
it('should return recommendations with installed skills', async () => {
|
|
149
|
+
const input = {
|
|
150
|
+
installed_skills: ['anthropic/commit'],
|
|
151
|
+
limit: 5,
|
|
152
|
+
};
|
|
153
|
+
const result = await executeRecommend(input, context);
|
|
154
|
+
recordTiming('recommend:with_installed', 'skill_recommend (installed)', result.timing.totalMs);
|
|
155
|
+
// May have 0 recommendations if seed data overlaps with installed skill
|
|
156
|
+
expect(result.recommendations.length).toBeGreaterThanOrEqual(0);
|
|
157
|
+
expect(result.context.installed_count).toBe(1);
|
|
158
|
+
// If there are recommendations, should not include already installed skill
|
|
159
|
+
const recommendedIds = result.recommendations.map((r) => r.skill_id);
|
|
160
|
+
expect(recommendedIds).not.toContain('anthropic/commit');
|
|
161
|
+
// Check for hardcoded values
|
|
162
|
+
const issues = scanResponseForHardcoded(result, 'skill_recommend');
|
|
163
|
+
expect(issues.filter((i) => i.severity === 'error')).toHaveLength(0);
|
|
164
|
+
});
|
|
165
|
+
it('should use project context for recommendations', async () => {
|
|
166
|
+
const input = {
|
|
167
|
+
installed_skills: [],
|
|
168
|
+
project_context: 'React TypeScript frontend with Jest testing',
|
|
169
|
+
limit: 5,
|
|
170
|
+
};
|
|
171
|
+
const result = await executeRecommend(input, context);
|
|
172
|
+
recordTiming('recommend:context', 'skill_recommend (context)', result.timing.totalMs);
|
|
173
|
+
expect(result.context.has_project_context).toBe(true);
|
|
174
|
+
// Check for hardcoded values
|
|
175
|
+
const issues = scanResponseForHardcoded(result, 'skill_recommend');
|
|
176
|
+
expect(issues.filter((i) => i.severity === 'error')).toHaveLength(0);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe('Auto-Detection of Installed Skills', () => {
|
|
180
|
+
it('should auto-detect installed skills from ~/.claude/skills/', async () => {
|
|
181
|
+
// Create mock installed skill
|
|
182
|
+
createMockInstalledSkill('test-installed-skill');
|
|
183
|
+
// Override HOME for this test
|
|
184
|
+
const originalHome = process.env.HOME;
|
|
185
|
+
process.env.HOME = TEST_DIR;
|
|
186
|
+
try {
|
|
187
|
+
const input = {
|
|
188
|
+
installed_skills: [], // Empty to trigger auto-detection
|
|
189
|
+
limit: 5,
|
|
190
|
+
};
|
|
191
|
+
const result = await executeRecommend(input, context);
|
|
192
|
+
// Should have auto-detected
|
|
193
|
+
expect(result.context.auto_detected).toBe(true);
|
|
194
|
+
// Check for hardcoded values
|
|
195
|
+
const issues = scanResponseForHardcoded(result, 'skill_recommend (auto-detect)');
|
|
196
|
+
expect(issues.filter((i) => i.severity === 'error')).toHaveLength(0);
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
process.env.HOME = originalHome;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe('Overlap Detection', () => {
|
|
204
|
+
it('should filter overlapping skills when enabled', async () => {
|
|
205
|
+
const input = {
|
|
206
|
+
installed_skills: ['community/jest-helper'],
|
|
207
|
+
detect_overlap: true,
|
|
208
|
+
limit: 10,
|
|
209
|
+
};
|
|
210
|
+
const result = await executeRecommend(input, context);
|
|
211
|
+
recordTiming('recommend:overlap', 'skill_recommend (overlap)', result.timing.totalMs);
|
|
212
|
+
// vitest-helper might be filtered as similar to jest-helper
|
|
213
|
+
// Just verify overlap_filtered is tracked
|
|
214
|
+
expect(typeof result.overlap_filtered).toBe('number');
|
|
215
|
+
// Check for hardcoded values
|
|
216
|
+
const issues = scanResponseForHardcoded(result, 'skill_recommend');
|
|
217
|
+
expect(issues.filter((i) => i.severity === 'error')).toHaveLength(0);
|
|
218
|
+
});
|
|
219
|
+
it('should not filter when detect_overlap is false', async () => {
|
|
220
|
+
const input = {
|
|
221
|
+
installed_skills: ['community/jest-helper'],
|
|
222
|
+
detect_overlap: false,
|
|
223
|
+
limit: 10,
|
|
224
|
+
};
|
|
225
|
+
const result = await executeRecommend(input, context);
|
|
226
|
+
// Should have more candidates when overlap not filtered
|
|
227
|
+
expect(result.candidates_considered).toBeGreaterThan(0);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
describe('Response Data Quality', () => {
|
|
231
|
+
it('should return valid recommendation structure', async () => {
|
|
232
|
+
const input = {
|
|
233
|
+
installed_skills: [],
|
|
234
|
+
limit: 3,
|
|
235
|
+
};
|
|
236
|
+
const result = await executeRecommend(input, context);
|
|
237
|
+
for (const rec of result.recommendations) {
|
|
238
|
+
// Validate structure
|
|
239
|
+
expect(rec.skill_id).toBeDefined();
|
|
240
|
+
expect(rec.name).toBeDefined();
|
|
241
|
+
expect(rec.reason).toBeDefined();
|
|
242
|
+
expect(rec.similarity_score).toBeGreaterThanOrEqual(0);
|
|
243
|
+
expect(rec.similarity_score).toBeLessThanOrEqual(1);
|
|
244
|
+
expect(['verified', 'community', 'standard', 'unverified']).toContain(rec.trust_tier);
|
|
245
|
+
expect(rec.quality_score).toBeGreaterThanOrEqual(0);
|
|
246
|
+
expect(rec.quality_score).toBeLessThanOrEqual(100);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
it('should not contain hardcoded skill IDs in recommendations', async () => {
|
|
250
|
+
const input = {
|
|
251
|
+
installed_skills: [],
|
|
252
|
+
limit: 10,
|
|
253
|
+
};
|
|
254
|
+
const result = await executeRecommend(input, context);
|
|
255
|
+
// All skill IDs should come from database, not hardcoded
|
|
256
|
+
const dbSkillIds = SEED_SKILLS.map((s) => s.id);
|
|
257
|
+
for (const rec of result.recommendations) {
|
|
258
|
+
expect(dbSkillIds).toContain(rec.skill_id);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
describe('Hardcoded Value Detection (SMI-902/904 Regression)', () => {
|
|
263
|
+
it('should not expose user paths in response', async () => {
|
|
264
|
+
const input = {
|
|
265
|
+
installed_skills: ['anthropic/commit'],
|
|
266
|
+
project_context: 'Testing for hardcoded paths',
|
|
267
|
+
limit: 5,
|
|
268
|
+
};
|
|
269
|
+
const result = await executeRecommend(input, context);
|
|
270
|
+
const responseStr = JSON.stringify(result);
|
|
271
|
+
// Should not contain user-specific paths
|
|
272
|
+
expect(responseStr).not.toMatch(/\/Users\/[a-zA-Z0-9_-]+\//);
|
|
273
|
+
expect(responseStr).not.toMatch(/\/home\/[a-zA-Z0-9_-]+\//);
|
|
274
|
+
expect(responseStr).not.toContain(homedir());
|
|
275
|
+
});
|
|
276
|
+
it('should not expose localhost URLs in response', async () => {
|
|
277
|
+
const input = {
|
|
278
|
+
installed_skills: [],
|
|
279
|
+
limit: 5,
|
|
280
|
+
};
|
|
281
|
+
const result = await executeRecommend(input, context);
|
|
282
|
+
const responseStr = JSON.stringify(result);
|
|
283
|
+
// Should not contain localhost
|
|
284
|
+
expect(responseStr).not.toMatch(/localhost:\d+/);
|
|
285
|
+
expect(responseStr).not.toMatch(/127\.0\.0\.1:\d+/);
|
|
286
|
+
});
|
|
287
|
+
it('should not expose API keys in response', async () => {
|
|
288
|
+
const input = {
|
|
289
|
+
installed_skills: [],
|
|
290
|
+
project_context: 'Should not leak credentials',
|
|
291
|
+
limit: 5,
|
|
292
|
+
};
|
|
293
|
+
const result = await executeRecommend(input, context);
|
|
294
|
+
const responseStr = JSON.stringify(result);
|
|
295
|
+
// Should not contain API keys
|
|
296
|
+
expect(responseStr).not.toMatch(/ghp_[a-zA-Z0-9]{36}/);
|
|
297
|
+
expect(responseStr).not.toMatch(/sk-[a-zA-Z0-9]{32,}/);
|
|
298
|
+
expect(responseStr).not.toMatch(/lin_api_[a-zA-Z0-9]+/);
|
|
299
|
+
});
|
|
300
|
+
it('should have dynamic recommendations not hardcoded static list', async () => {
|
|
301
|
+
// Run twice with different contexts
|
|
302
|
+
const result1 = await executeRecommend({ installed_skills: [], project_context: 'Docker DevOps', limit: 5 }, context);
|
|
303
|
+
const result2 = await executeRecommend({ installed_skills: [], project_context: 'React testing', limit: 5 }, context);
|
|
304
|
+
// Results should potentially differ based on context
|
|
305
|
+
// (This test helps detect if recommendations are completely static)
|
|
306
|
+
const ids1 = result1.recommendations.map((r) => r.skill_id).sort();
|
|
307
|
+
const ids2 = result2.recommendations.map((r) => r.skill_id).sort();
|
|
308
|
+
// If results are EXACTLY the same regardless of context, may indicate hardcoding
|
|
309
|
+
// Note: This is a soft check - might be same due to small dataset
|
|
310
|
+
if (ids1.join(',') === ids2.join(',')) {
|
|
311
|
+
console.warn('Warning: Recommendations identical for different contexts - may indicate hardcoding');
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
describe('Performance Baselines', () => {
|
|
316
|
+
it('should complete recommendation in reasonable time', async () => {
|
|
317
|
+
const { durationMs } = await measureAsync('recommend:baseline', 'skill_recommend baseline', () => executeRecommend({
|
|
318
|
+
installed_skills: ['anthropic/commit'],
|
|
319
|
+
project_context: 'Full-stack TypeScript project',
|
|
320
|
+
limit: 10,
|
|
321
|
+
}, context));
|
|
322
|
+
// Baseline: should complete within 2 seconds for seed data
|
|
323
|
+
expect(durationMs).toBeLessThan(2000);
|
|
324
|
+
console.log(`Recommend baseline: ${durationMs}ms`);
|
|
325
|
+
});
|
|
326
|
+
it('should handle multiple rapid requests', async () => {
|
|
327
|
+
const requests = Array(5)
|
|
328
|
+
.fill(null)
|
|
329
|
+
.map((_, i) => executeRecommend({ installed_skills: [], project_context: `Context ${i}`, limit: 3 }, context));
|
|
330
|
+
const results = await Promise.all(requests);
|
|
331
|
+
expect(results).toHaveLength(5);
|
|
332
|
+
for (const result of results) {
|
|
333
|
+
expect(result.recommendations.length).toBeGreaterThanOrEqual(0);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
describe('Error Handling', () => {
|
|
338
|
+
it('should handle invalid limit gracefully', async () => {
|
|
339
|
+
// Zod should enforce limits
|
|
340
|
+
await expect(executeRecommend({ installed_skills: [], limit: 100 }, context)).rejects.toThrow();
|
|
341
|
+
});
|
|
342
|
+
it('should handle invalid min_similarity gracefully', async () => {
|
|
343
|
+
await expect(executeRecommend({ installed_skills: [], min_similarity: 2.0 }, context)).rejects.toThrow();
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
//# sourceMappingURL=recommend.e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommend.e2e.test.js","sourceRoot":"","sources":["../../../tests/e2e/recommend.e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E Test: Search → Get → Install Flow
|
|
3
|
+
*
|
|
4
|
+
* Tests the complete user journey from discovering a skill to installing it.
|
|
5
|
+
* Uses seed data to ensure consistent, reproducible tests.
|
|
6
|
+
*
|
|
7
|
+
* @see SMI-796: E2E test: Search → Get → Install flow
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=skill-flow.e2e.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-flow.e2e.test.d.ts","sourceRoot":"","sources":["../../../tests/e2e/skill-flow.e2e.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E Test: Search → Get → Install Flow
|
|
3
|
+
*
|
|
4
|
+
* Tests the complete user journey from discovering a skill to installing it.
|
|
5
|
+
* Uses seed data to ensure consistent, reproducible tests.
|
|
6
|
+
*
|
|
7
|
+
* @see SMI-796: E2E test: Search → Get → Install flow
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
10
|
+
import { existsSync, rmSync, mkdirSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { tmpdir } from 'os';
|
|
13
|
+
import { createDatabase, initializeSchema, SkillRepository, } from '@skillsmith/core';
|
|
14
|
+
import { createToolContext } from '../../src/context.js';
|
|
15
|
+
import { executeSearch } from '../../src/tools/search.js';
|
|
16
|
+
import { executeGetSkill } from '../../src/tools/get-skill.js';
|
|
17
|
+
const SEED_SKILLS = [
|
|
18
|
+
{
|
|
19
|
+
id: 'anthropic/commit',
|
|
20
|
+
name: 'commit',
|
|
21
|
+
description: 'Generate semantic commit messages following conventional commits',
|
|
22
|
+
author: 'anthropic',
|
|
23
|
+
repoUrl: 'https://github.com/anthropics/claude-code/tree/main/skills/commit',
|
|
24
|
+
qualityScore: 0.95,
|
|
25
|
+
trustTier: 'verified',
|
|
26
|
+
tags: ['development', 'git', 'commit', 'conventional-commits'],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'community/jest-helper',
|
|
30
|
+
name: 'jest-helper',
|
|
31
|
+
description: 'Generate Jest test cases for React components',
|
|
32
|
+
author: 'community',
|
|
33
|
+
repoUrl: 'https://github.com/skillsmith-community/jest-helper',
|
|
34
|
+
qualityScore: 0.87,
|
|
35
|
+
trustTier: 'community',
|
|
36
|
+
tags: ['testing', 'jest', 'react', 'unit-tests'],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'community/vitest-helper',
|
|
40
|
+
name: 'vitest-helper',
|
|
41
|
+
description: 'Generate Vitest test cases with modern ESM support',
|
|
42
|
+
author: 'community',
|
|
43
|
+
repoUrl: 'https://github.com/skillsmith-community/vitest-helper',
|
|
44
|
+
qualityScore: 0.85,
|
|
45
|
+
trustTier: 'community',
|
|
46
|
+
tags: ['testing', 'vitest', 'esm', 'typescript'],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'community/docker-compose',
|
|
50
|
+
name: 'docker-compose',
|
|
51
|
+
description: 'Generate and manage Docker Compose configurations',
|
|
52
|
+
author: 'community',
|
|
53
|
+
repoUrl: 'https://github.com/skillsmith-community/docker-compose',
|
|
54
|
+
qualityScore: 0.84,
|
|
55
|
+
trustTier: 'community',
|
|
56
|
+
tags: ['devops', 'docker', 'containers'],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
describe('E2E: Skill Discovery Flow', () => {
|
|
60
|
+
let db;
|
|
61
|
+
let context;
|
|
62
|
+
let testDbPath;
|
|
63
|
+
beforeAll(() => {
|
|
64
|
+
// Create isolated test database
|
|
65
|
+
const testDir = join(tmpdir(), 'skillsmith-e2e-test');
|
|
66
|
+
if (!existsSync(testDir)) {
|
|
67
|
+
mkdirSync(testDir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
testDbPath = join(testDir, `e2e-test-${Date.now()}.db`);
|
|
70
|
+
// Initialize database with seed data
|
|
71
|
+
db = createDatabase(testDbPath);
|
|
72
|
+
initializeSchema(db);
|
|
73
|
+
const skillRepository = new SkillRepository(db);
|
|
74
|
+
for (const skill of SEED_SKILLS) {
|
|
75
|
+
skillRepository.create(skill);
|
|
76
|
+
}
|
|
77
|
+
// Create context
|
|
78
|
+
context = createToolContext({ dbPath: testDbPath });
|
|
79
|
+
});
|
|
80
|
+
afterAll(() => {
|
|
81
|
+
db?.close();
|
|
82
|
+
if (testDbPath && existsSync(testDbPath)) {
|
|
83
|
+
rmSync(testDbPath, { force: true });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
describe('Step 1: Search for skills', () => {
|
|
87
|
+
it('should find commit skill when searching for "commit"', async () => {
|
|
88
|
+
const input = { query: 'commit' };
|
|
89
|
+
const result = await executeSearch(input, context);
|
|
90
|
+
expect(result.results.length).toBeGreaterThan(0);
|
|
91
|
+
expect(result.results.some((s) => s.id === 'anthropic/commit')).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
it('should find testing skills when searching for "test"', async () => {
|
|
94
|
+
const input = { query: 'test' };
|
|
95
|
+
const result = await executeSearch(input, context);
|
|
96
|
+
expect(result.results.length).toBeGreaterThanOrEqual(2);
|
|
97
|
+
expect(result.results.some((s) => s.id === 'community/jest-helper')).toBe(true);
|
|
98
|
+
expect(result.results.some((s) => s.id === 'community/vitest-helper')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
it('should filter by category', async () => {
|
|
101
|
+
// Note: Category filtering may not reduce results if skills already match the category
|
|
102
|
+
const input = { query: 'docker' };
|
|
103
|
+
const result = await executeSearch(input, context);
|
|
104
|
+
expect(result.results.length).toBeGreaterThan(0);
|
|
105
|
+
expect(result.results.some((s) => s.id === 'community/docker-compose')).toBe(true);
|
|
106
|
+
// Verify docker-compose has expected category from extractCategoryFromTags
|
|
107
|
+
const dockerSkill = result.results.find((s) => s.id === 'community/docker-compose');
|
|
108
|
+
expect(dockerSkill?.category).toBe('devops');
|
|
109
|
+
});
|
|
110
|
+
it('should filter by trust tier', async () => {
|
|
111
|
+
const input = { query: 'commit', trust_tier: 'verified' };
|
|
112
|
+
const result = await executeSearch(input, context);
|
|
113
|
+
expect(result.results.every((s) => s.trustTier === 'verified')).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
it('should filter by minimum score', async () => {
|
|
116
|
+
const input = { query: 'test', min_score: 85 };
|
|
117
|
+
const result = await executeSearch(input, context);
|
|
118
|
+
expect(result.results.every((s) => s.score >= 85)).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe('Step 2: Get skill details', () => {
|
|
122
|
+
it('should retrieve full details for a skill', async () => {
|
|
123
|
+
const input = { id: 'anthropic/commit' };
|
|
124
|
+
const result = await executeGetSkill(input, context);
|
|
125
|
+
expect(result.skill.id).toBe('anthropic/commit');
|
|
126
|
+
expect(result.skill.name).toBe('commit');
|
|
127
|
+
expect(result.skill.author).toBe('anthropic');
|
|
128
|
+
expect(result.skill.trustTier).toBe('verified');
|
|
129
|
+
expect(result.skill.score).toBe(95);
|
|
130
|
+
expect(result.installCommand).toContain('claude skill add anthropic/commit');
|
|
131
|
+
});
|
|
132
|
+
it('should include timing information', async () => {
|
|
133
|
+
const input = { id: 'community/jest-helper' };
|
|
134
|
+
const result = await executeGetSkill(input, context);
|
|
135
|
+
expect(result.timing).toBeDefined();
|
|
136
|
+
expect(result.timing.totalMs).toBeGreaterThanOrEqual(0);
|
|
137
|
+
});
|
|
138
|
+
it('should throw for non-existent skill', async () => {
|
|
139
|
+
const input = { id: 'nonexistent/skill' };
|
|
140
|
+
await expect(executeGetSkill(input, context)).rejects.toThrow();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('Step 3: Search → Get flow integration', () => {
|
|
144
|
+
it('should allow getting details of any search result', async () => {
|
|
145
|
+
// Step 1: Search
|
|
146
|
+
const searchInput = { query: 'jest' };
|
|
147
|
+
const searchResult = await executeSearch(searchInput, context);
|
|
148
|
+
expect(searchResult.results.length).toBeGreaterThan(0);
|
|
149
|
+
// Step 2: Get details of first result
|
|
150
|
+
const firstResult = searchResult.results[0];
|
|
151
|
+
const getInput = { id: firstResult.id };
|
|
152
|
+
const skillDetails = await executeGetSkill(getInput, context);
|
|
153
|
+
// Verify consistency
|
|
154
|
+
expect(skillDetails.skill.id).toBe(firstResult.id);
|
|
155
|
+
expect(skillDetails.skill.name).toBe(firstResult.name);
|
|
156
|
+
expect(skillDetails.skill.trustTier).toBe(firstResult.trustTier);
|
|
157
|
+
});
|
|
158
|
+
it('should provide install command for all skills', async () => {
|
|
159
|
+
// Search for all testing skills
|
|
160
|
+
const searchInput = { query: 'test' };
|
|
161
|
+
const searchResult = await executeSearch(searchInput, context);
|
|
162
|
+
// Get details and verify install commands
|
|
163
|
+
for (const result of searchResult.results) {
|
|
164
|
+
const details = await executeGetSkill({ id: result.id }, context);
|
|
165
|
+
expect(details.installCommand).toMatch(/^claude skill add [a-z0-9-]+\/[a-z0-9-]+$/i);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe('Quality and Performance', () => {
|
|
170
|
+
it('should complete search in under 100ms', async () => {
|
|
171
|
+
const input = { query: 'commit' };
|
|
172
|
+
const result = await executeSearch(input, context);
|
|
173
|
+
expect(result.timing.totalMs).toBeLessThan(100);
|
|
174
|
+
});
|
|
175
|
+
it('should complete get-skill in under 50ms', async () => {
|
|
176
|
+
const input = { id: 'anthropic/commit' };
|
|
177
|
+
const result = await executeGetSkill(input, context);
|
|
178
|
+
expect(result.timing.totalMs).toBeLessThan(50);
|
|
179
|
+
});
|
|
180
|
+
it('should handle rapid successive searches', async () => {
|
|
181
|
+
const queries = ['commit', 'test', 'docker', 'jest', 'vitest'];
|
|
182
|
+
const results = await Promise.all(queries.map((query) => executeSearch({ query }, context)));
|
|
183
|
+
expect(results.every((r) => r.results.length >= 0)).toBe(true);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('Error Handling', () => {
|
|
187
|
+
it('should reject empty search queries', async () => {
|
|
188
|
+
const input = { query: '' };
|
|
189
|
+
await expect(executeSearch(input, context)).rejects.toThrow();
|
|
190
|
+
});
|
|
191
|
+
it('should reject single-character search queries', async () => {
|
|
192
|
+
const input = { query: 'a' };
|
|
193
|
+
await expect(executeSearch(input, context)).rejects.toThrow();
|
|
194
|
+
});
|
|
195
|
+
it('should reject invalid skill ID formats', async () => {
|
|
196
|
+
const input = { id: 'invalid-format' };
|
|
197
|
+
await expect(executeGetSkill(input, context)).rejects.toThrow();
|
|
198
|
+
});
|
|
199
|
+
it('should reject out-of-range min_score', async () => {
|
|
200
|
+
const input = { query: 'test', min_score: 150 };
|
|
201
|
+
await expect(executeSearch(input, context)).rejects.toThrow();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe('E2E: Data Quality Validation', () => {
|
|
206
|
+
let db;
|
|
207
|
+
let context;
|
|
208
|
+
let testDbPath;
|
|
209
|
+
beforeAll(() => {
|
|
210
|
+
const testDir = join(tmpdir(), 'skillsmith-e2e-quality');
|
|
211
|
+
if (!existsSync(testDir)) {
|
|
212
|
+
mkdirSync(testDir, { recursive: true });
|
|
213
|
+
}
|
|
214
|
+
testDbPath = join(testDir, `quality-test-${Date.now()}.db`);
|
|
215
|
+
db = createDatabase(testDbPath);
|
|
216
|
+
initializeSchema(db);
|
|
217
|
+
const skillRepository = new SkillRepository(db);
|
|
218
|
+
for (const skill of SEED_SKILLS) {
|
|
219
|
+
skillRepository.create(skill);
|
|
220
|
+
}
|
|
221
|
+
context = createToolContext({ dbPath: testDbPath });
|
|
222
|
+
});
|
|
223
|
+
afterAll(() => {
|
|
224
|
+
db?.close();
|
|
225
|
+
if (testDbPath && existsSync(testDbPath)) {
|
|
226
|
+
rmSync(testDbPath, { force: true });
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
describe('SMI-795: Import Quality Validation', () => {
|
|
230
|
+
it('should have valid trust tiers for all skills', async () => {
|
|
231
|
+
// Since empty/short queries throw, get skills directly
|
|
232
|
+
for (const skill of SEED_SKILLS) {
|
|
233
|
+
const details = await executeGetSkill({ id: skill.id }, context);
|
|
234
|
+
expect(['verified', 'community', 'standard', 'unverified', 'experimental']).toContain(details.skill.trustTier);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
it('should have quality scores between 0 and 100', async () => {
|
|
238
|
+
for (const skill of SEED_SKILLS) {
|
|
239
|
+
const details = await executeGetSkill({ id: skill.id }, context);
|
|
240
|
+
const score = details.skill.score ?? 0;
|
|
241
|
+
expect(score).toBeGreaterThanOrEqual(0);
|
|
242
|
+
expect(score).toBeLessThanOrEqual(100);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
it('should have non-empty descriptions', async () => {
|
|
246
|
+
for (const skill of SEED_SKILLS) {
|
|
247
|
+
const details = await executeGetSkill({ id: skill.id }, context);
|
|
248
|
+
const description = details.skill.description ?? '';
|
|
249
|
+
expect(description.length).toBeGreaterThan(0);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
it('should have valid category mappings', async () => {
|
|
253
|
+
for (const skill of SEED_SKILLS) {
|
|
254
|
+
const details = await executeGetSkill({ id: skill.id }, context);
|
|
255
|
+
const category = details.skill.category ?? 'other';
|
|
256
|
+
expect([
|
|
257
|
+
'development',
|
|
258
|
+
'testing',
|
|
259
|
+
'documentation',
|
|
260
|
+
'devops',
|
|
261
|
+
'database',
|
|
262
|
+
'security',
|
|
263
|
+
'productivity',
|
|
264
|
+
'integration',
|
|
265
|
+
'ai-ml',
|
|
266
|
+
'other',
|
|
267
|
+
]).toContain(category);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
it('should have valid author names', async () => {
|
|
271
|
+
for (const skill of SEED_SKILLS) {
|
|
272
|
+
const details = await executeGetSkill({ id: skill.id }, context);
|
|
273
|
+
const author = details.skill.author ?? '';
|
|
274
|
+
expect(author).not.toBe('unknown');
|
|
275
|
+
expect(author.length).toBeGreaterThan(0);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
//# sourceMappingURL=skill-flow.e2e.test.js.map
|