@skillsmith/cli 0.5.0 → 0.5.1

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.
Files changed (66) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/src/commands/audit.test.js +1 -1
  3. package/dist/src/commands/audit.test.js.map +1 -1
  4. package/dist/src/commands/create.d.ts.map +1 -1
  5. package/dist/src/commands/create.js +1 -1
  6. package/dist/src/commands/create.js.map +1 -1
  7. package/dist/src/commands/index.d.ts +1 -0
  8. package/dist/src/commands/index.d.ts.map +1 -1
  9. package/dist/src/commands/index.js +3 -1
  10. package/dist/src/commands/index.js.map +1 -1
  11. package/dist/src/commands/install-skill.d.ts.map +1 -1
  12. package/dist/src/commands/install-skill.js +10 -3
  13. package/dist/src/commands/install-skill.js.map +1 -1
  14. package/dist/src/commands/install.d.ts +13 -0
  15. package/dist/src/commands/install.d.ts.map +1 -0
  16. package/dist/src/commands/install.js +213 -0
  17. package/dist/src/commands/install.js.map +1 -0
  18. package/dist/src/commands/manage.d.ts.map +1 -1
  19. package/dist/src/commands/manage.js +55 -19
  20. package/dist/src/commands/manage.js.map +1 -1
  21. package/dist/src/commands/pin.js +1 -1
  22. package/dist/src/commands/pin.js.map +1 -1
  23. package/dist/src/commands/search.d.ts.map +1 -1
  24. package/dist/src/commands/search.js +43 -3
  25. package/dist/src/commands/search.js.map +1 -1
  26. package/dist/src/index.d.ts +2 -1
  27. package/dist/src/index.d.ts.map +1 -1
  28. package/dist/src/index.js +8 -3
  29. package/dist/src/index.js.map +1 -1
  30. package/dist/src/templates/readme.md.template.d.ts +1 -1
  31. package/dist/src/templates/readme.md.template.d.ts.map +1 -1
  32. package/dist/src/templates/readme.md.template.js +1 -1
  33. package/dist/src/templates/skill.md.template.d.ts +1 -1
  34. package/dist/src/templates/skill.md.template.d.ts.map +1 -1
  35. package/dist/src/templates/skill.md.template.js +1 -1
  36. package/dist/src/utils/skills-directory.d.ts.map +1 -1
  37. package/dist/src/utils/skills-directory.js +3 -2
  38. package/dist/src/utils/skills-directory.js.map +1 -1
  39. package/dist/tests/install-skill.test.js +9 -4
  40. package/dist/tests/install-skill.test.js.map +1 -1
  41. package/dist/tests/license-validation.test.d.ts +8 -0
  42. package/dist/tests/license-validation.test.d.ts.map +1 -0
  43. package/dist/tests/license-validation.test.js +186 -0
  44. package/dist/tests/license-validation.test.js.map +1 -0
  45. package/dist/tests/manage.test.js +84 -0
  46. package/dist/tests/manage.test.js.map +1 -1
  47. package/dist/tests/recommend-helpers.test.d.ts +9 -0
  48. package/dist/tests/recommend-helpers.test.d.ts.map +1 -0
  49. package/dist/tests/recommend-helpers.test.js +308 -0
  50. package/dist/tests/recommend-helpers.test.js.map +1 -0
  51. package/dist/tests/recommend-scoring.test.d.ts +8 -0
  52. package/dist/tests/recommend-scoring.test.d.ts.map +1 -0
  53. package/dist/tests/recommend-scoring.test.js +184 -0
  54. package/dist/tests/recommend-scoring.test.js.map +1 -0
  55. package/dist/tests/tool-analyzer.test.d.ts +8 -0
  56. package/dist/tests/tool-analyzer.test.d.ts.map +1 -0
  57. package/dist/tests/tool-analyzer.test.js +149 -0
  58. package/dist/tests/tool-analyzer.test.js.map +1 -0
  59. package/dist/tests/unit/commands/install.test.d.ts +7 -0
  60. package/dist/tests/unit/commands/install.test.d.ts.map +1 -0
  61. package/dist/tests/unit/commands/install.test.js +407 -0
  62. package/dist/tests/unit/commands/install.test.js.map +1 -0
  63. package/dist/vitest.config.d.ts.map +1 -1
  64. package/dist/vitest.config.js +9 -3
  65. package/dist/vitest.config.js.map +1 -1
  66. package/package.json +4 -3
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Unit tests for recommend.helpers.ts pure logic functions
3
+ *
4
+ * Tests validateTrustTier, isNetworkError, buildStackFromAnalysis,
5
+ * formatAsJson, formatOfflineResults, formatRecommendations.
6
+ * No mocking required — all functions are pure.
7
+ */
8
+ import { describe, it, expect } from 'vitest';
9
+ import { validateTrustTier, isNetworkError, buildStackFromAnalysis, formatAsJson, formatOfflineResults, formatRecommendations, } from '../src/commands/recommend.helpers.js';
10
+ // ============================================================================
11
+ // Helpers
12
+ // ============================================================================
13
+ /** Minimal CodebaseContext with only the fields the helpers actually read */
14
+ function makeContext(overrides) {
15
+ return {
16
+ frameworks: [
17
+ { name: 'React', confidence: 0.95, evidence: [] },
18
+ { name: 'Vitest', confidence: 0.8, evidence: [] },
19
+ ],
20
+ dependencies: [
21
+ { name: 'react', isDev: false, version: '18.0.0' },
22
+ { name: 'vitest', isDev: true, version: '1.0.0' },
23
+ { name: 'express', isDev: false, version: '4.0.0' },
24
+ ],
25
+ stats: { totalFiles: 42, totalLines: 5000 },
26
+ ...overrides,
27
+ };
28
+ }
29
+ function makeResponse(overrides) {
30
+ return {
31
+ recommendations: [
32
+ {
33
+ skill_id: 'acme/linter',
34
+ name: 'linter',
35
+ reason: 'Improves code quality',
36
+ similarity_score: 0.85,
37
+ trust_tier: 'verified',
38
+ quality_score: 92,
39
+ roles: ['code-quality'],
40
+ },
41
+ ],
42
+ candidates_considered: 50,
43
+ overlap_filtered: 2,
44
+ role_filtered: 1,
45
+ context: {
46
+ installed_count: 3,
47
+ has_project_context: true,
48
+ using_semantic_matching: true,
49
+ auto_detected: true,
50
+ },
51
+ timing: { totalMs: 123 },
52
+ ...overrides,
53
+ };
54
+ }
55
+ // ============================================================================
56
+ // validateTrustTier
57
+ // ============================================================================
58
+ describe('validateTrustTier', () => {
59
+ it('accepts "verified"', () => {
60
+ expect(validateTrustTier('verified')).toBe('verified');
61
+ });
62
+ it('accepts "community"', () => {
63
+ expect(validateTrustTier('community')).toBe('community');
64
+ });
65
+ it('accepts "experimental"', () => {
66
+ expect(validateTrustTier('experimental')).toBe('experimental');
67
+ });
68
+ it('accepts "unknown"', () => {
69
+ expect(validateTrustTier('unknown')).toBe('unknown');
70
+ });
71
+ it('returns "unknown" for invalid string', () => {
72
+ expect(validateTrustTier('invalid')).toBe('unknown');
73
+ });
74
+ it('returns "unknown" for non-string input', () => {
75
+ expect(validateTrustTier(42)).toBe('unknown');
76
+ expect(validateTrustTier(null)).toBe('unknown');
77
+ expect(validateTrustTier(undefined)).toBe('unknown');
78
+ });
79
+ });
80
+ // ============================================================================
81
+ // isNetworkError
82
+ // ============================================================================
83
+ describe('isNetworkError', () => {
84
+ it('detects "fetch failed"', () => {
85
+ expect(isNetworkError(new Error('fetch failed'))).toBe(true);
86
+ });
87
+ it('detects "network" errors', () => {
88
+ expect(isNetworkError(new Error('Network error occurred'))).toBe(true);
89
+ });
90
+ it('detects ENOTFOUND', () => {
91
+ expect(isNetworkError(new Error('getaddrinfo ENOTFOUND api.example.com'))).toBe(true);
92
+ });
93
+ it('detects ECONNREFUSED', () => {
94
+ expect(isNetworkError(new Error('connect ECONNREFUSED 127.0.0.1:3000'))).toBe(true);
95
+ });
96
+ it('detects timeout', () => {
97
+ expect(isNetworkError(new Error('request timeout'))).toBe(true);
98
+ });
99
+ it('detects socket errors', () => {
100
+ expect(isNetworkError(new Error('socket hang up'))).toBe(true);
101
+ });
102
+ it('detects AbortError by name', () => {
103
+ const err = new Error('aborted');
104
+ err.name = 'AbortError';
105
+ expect(isNetworkError(err)).toBe(true);
106
+ });
107
+ it('returns false for non-network errors', () => {
108
+ expect(isNetworkError(new Error('syntax error'))).toBe(false);
109
+ });
110
+ it('returns false for non-Error values', () => {
111
+ expect(isNetworkError('string')).toBe(false);
112
+ expect(isNetworkError(42)).toBe(false);
113
+ expect(isNetworkError(null)).toBe(false);
114
+ });
115
+ });
116
+ // ============================================================================
117
+ // buildStackFromAnalysis
118
+ // ============================================================================
119
+ describe('buildStackFromAnalysis', () => {
120
+ it('builds stack from frameworks and prod dependencies', () => {
121
+ const ctx = makeContext();
122
+ const stack = buildStackFromAnalysis(ctx);
123
+ expect(stack).toContain('react');
124
+ expect(stack).toContain('vitest');
125
+ expect(stack).toContain('express');
126
+ });
127
+ it('excludes dev dependencies', () => {
128
+ const ctx = makeContext({
129
+ dependencies: [{ name: 'vitest', isDev: true, version: '1.0.0' }],
130
+ frameworks: [],
131
+ });
132
+ const stack = buildStackFromAnalysis(ctx);
133
+ expect(stack).not.toContain('vitest');
134
+ });
135
+ it('deduplicates entries', () => {
136
+ const ctx = makeContext({
137
+ frameworks: [{ name: 'React', confidence: 0.9, evidence: [] }],
138
+ dependencies: [{ name: 'react', isDev: false, version: '18.0.0' }],
139
+ });
140
+ const stack = buildStackFromAnalysis(ctx);
141
+ const reactCount = stack.filter((s) => s === 'react').length;
142
+ expect(reactCount).toBe(1);
143
+ });
144
+ it('caps output at 10 items', () => {
145
+ const deps = Array.from({ length: 20 }, (_, i) => ({
146
+ name: `dep-${i}`,
147
+ isDev: false,
148
+ version: '1.0.0',
149
+ }));
150
+ const ctx = makeContext({ dependencies: deps, frameworks: [] });
151
+ const stack = buildStackFromAnalysis(ctx);
152
+ expect(stack.length).toBeLessThanOrEqual(10);
153
+ });
154
+ it('returns empty array for empty context', () => {
155
+ const ctx = makeContext({ frameworks: [], dependencies: [] });
156
+ expect(buildStackFromAnalysis(ctx)).toEqual([]);
157
+ });
158
+ });
159
+ // ============================================================================
160
+ // formatAsJson
161
+ // ============================================================================
162
+ describe('formatAsJson', () => {
163
+ it('returns valid JSON', () => {
164
+ const output = formatAsJson(makeResponse(), makeContext());
165
+ expect(() => JSON.parse(output)).not.toThrow();
166
+ });
167
+ it('includes recommendations in output', () => {
168
+ const parsed = JSON.parse(formatAsJson(makeResponse(), makeContext()));
169
+ expect(parsed.recommendations).toHaveLength(1);
170
+ expect(parsed.recommendations[0].name).toBe('linter');
171
+ });
172
+ it('includes analysis when context provided', () => {
173
+ const parsed = JSON.parse(formatAsJson(makeResponse(), makeContext()));
174
+ expect(parsed.analysis).not.toBeNull();
175
+ expect(parsed.analysis.frameworks).toHaveLength(2);
176
+ expect(parsed.analysis.frameworks[0].confidence).toBe(95);
177
+ });
178
+ it('sets analysis to null when no context', () => {
179
+ const parsed = JSON.parse(formatAsJson(makeResponse(), null));
180
+ expect(parsed.analysis).toBeNull();
181
+ });
182
+ it('includes meta fields', () => {
183
+ const parsed = JSON.parse(formatAsJson(makeResponse(), null));
184
+ expect(parsed.meta.candidates_considered).toBe(50);
185
+ expect(parsed.meta.overlap_filtered).toBe(2);
186
+ expect(parsed.meta.role_filtered).toBe(1);
187
+ expect(parsed.meta.timing_ms).toBe(123);
188
+ });
189
+ it('separates prod and dev dependencies', () => {
190
+ const parsed = JSON.parse(formatAsJson(makeResponse(), makeContext()));
191
+ const vitest = parsed.analysis.dependencies.find((d) => d.name === 'vitest');
192
+ expect(vitest.is_dev).toBe(true);
193
+ });
194
+ });
195
+ // ============================================================================
196
+ // formatRecommendations
197
+ // ============================================================================
198
+ describe('formatRecommendations', () => {
199
+ it('includes recommendation count', () => {
200
+ const output = formatRecommendations(makeResponse(), makeContext());
201
+ expect(output).toContain('1');
202
+ expect(output).toContain('recommendation');
203
+ });
204
+ it('includes skill name', () => {
205
+ const output = formatRecommendations(makeResponse(), null);
206
+ expect(output).toContain('linter');
207
+ });
208
+ it('includes candidates considered', () => {
209
+ const output = formatRecommendations(makeResponse(), null);
210
+ expect(output).toContain('50');
211
+ });
212
+ it('shows detected frameworks from context', () => {
213
+ const output = formatRecommendations(makeResponse(), makeContext());
214
+ expect(output).toContain('React');
215
+ });
216
+ it('shows suggestions when no recommendations', () => {
217
+ const response = makeResponse({ recommendations: [] });
218
+ const output = formatRecommendations(response, null);
219
+ expect(output).toContain('No recommendations found');
220
+ expect(output).toContain('package.json');
221
+ });
222
+ it('shows role filter hint when active and no results', () => {
223
+ const response = makeResponse({
224
+ recommendations: [],
225
+ context: {
226
+ installed_count: 0,
227
+ has_project_context: true,
228
+ using_semantic_matching: true,
229
+ auto_detected: false,
230
+ role_filter: 'testing',
231
+ },
232
+ });
233
+ const output = formatRecommendations(response, null);
234
+ expect(output).toContain('--role');
235
+ expect(output).toContain('testing');
236
+ });
237
+ it('shows overlap filtered count', () => {
238
+ const output = formatRecommendations(makeResponse(), null);
239
+ expect(output).toContain('2');
240
+ });
241
+ it('shows timing', () => {
242
+ const output = formatRecommendations(makeResponse(), null);
243
+ expect(output).toContain('123ms');
244
+ });
245
+ it('displays N/A for negative similarity scores', () => {
246
+ const response = makeResponse({
247
+ recommendations: [
248
+ {
249
+ skill_id: 'a/b',
250
+ name: 'test',
251
+ reason: 'r',
252
+ similarity_score: -1,
253
+ trust_tier: 'community',
254
+ quality_score: 50,
255
+ },
256
+ ],
257
+ });
258
+ const output = formatRecommendations(response, null);
259
+ expect(output).toContain('N/A');
260
+ });
261
+ });
262
+ // ============================================================================
263
+ // formatOfflineResults
264
+ // ============================================================================
265
+ describe('formatOfflineResults', () => {
266
+ it('returns JSON when json flag is true', () => {
267
+ const output = formatOfflineResults(makeContext(), true);
268
+ const parsed = JSON.parse(output);
269
+ expect(parsed.offline).toBe(true);
270
+ expect(parsed.analysis.frameworks).toHaveLength(2);
271
+ expect(parsed.message).toContain('Unable to reach');
272
+ });
273
+ it('includes stats in JSON output', () => {
274
+ const parsed = JSON.parse(formatOfflineResults(makeContext(), true));
275
+ expect(parsed.analysis.stats.total_files).toBe(42);
276
+ expect(parsed.analysis.stats.total_lines).toBe(5000);
277
+ });
278
+ it('shows offline warning in text mode', () => {
279
+ const output = formatOfflineResults(makeContext(), false);
280
+ expect(output).toContain('Unable to reach Skillsmith API');
281
+ });
282
+ it('shows frameworks in text mode', () => {
283
+ const output = formatOfflineResults(makeContext(), false);
284
+ expect(output).toContain('React');
285
+ expect(output).toContain('95%');
286
+ });
287
+ it('shows prod dependencies in text mode', () => {
288
+ const output = formatOfflineResults(makeContext(), false);
289
+ expect(output).toContain('react');
290
+ expect(output).toContain('express');
291
+ });
292
+ it('shows file stats in text mode', () => {
293
+ const output = formatOfflineResults(makeContext(), false);
294
+ expect(output).toContain('42');
295
+ expect(output).toContain('5,000');
296
+ });
297
+ it('handles empty frameworks', () => {
298
+ const ctx = makeContext({ frameworks: [] });
299
+ const output = formatOfflineResults(ctx, false);
300
+ expect(output).not.toContain('Detected Frameworks');
301
+ });
302
+ it('handles empty dependencies', () => {
303
+ const ctx = makeContext({ dependencies: [] });
304
+ const output = formatOfflineResults(ctx, false);
305
+ expect(output).not.toContain('Key Dependencies');
306
+ });
307
+ });
308
+ //# sourceMappingURL=recommend-helpers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommend-helpers.test.js","sourceRoot":"","sources":["../../tests/recommend-helpers.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Unit tests for recommend-scoring.ts pure logic functions
3
+ *
4
+ * Tests inferRolesFromTags, normalizeSkillName, skillsOverlap, filterOverlappingSkills.
5
+ * No mocking required — all functions are pure.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=recommend-scoring.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommend-scoring.test.d.ts","sourceRoot":"","sources":["../../tests/recommend-scoring.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Unit tests for recommend-scoring.ts pure logic functions
3
+ *
4
+ * Tests inferRolesFromTags, normalizeSkillName, skillsOverlap, filterOverlappingSkills.
5
+ * No mocking required — all functions are pure.
6
+ */
7
+ import { describe, it, expect } from 'vitest';
8
+ import { inferRolesFromTags, normalizeSkillName, skillsOverlap, filterOverlappingSkills, } from '../src/commands/recommend-scoring.js';
9
+ // ============================================================================
10
+ // inferRolesFromTags
11
+ // ============================================================================
12
+ describe('inferRolesFromTags', () => {
13
+ it('returns empty array for empty tags', () => {
14
+ expect(inferRolesFromTags([])).toEqual([]);
15
+ });
16
+ it('returns empty array for unrecognized tags', () => {
17
+ expect(inferRolesFromTags(['banana', 'spaceship'])).toEqual([]);
18
+ });
19
+ it('infers code-quality from lint-related tags', () => {
20
+ const roles = inferRolesFromTags(['lint']);
21
+ expect(roles).toContain('code-quality');
22
+ });
23
+ it('infers testing from test-related tags', () => {
24
+ const roles = inferRolesFromTags(['vitest']);
25
+ expect(roles).toContain('testing');
26
+ });
27
+ it('infers documentation from docs tags', () => {
28
+ const roles = inferRolesFromTags(['jsdoc']);
29
+ expect(roles).toContain('documentation');
30
+ });
31
+ it('infers workflow from CI/CD tags', () => {
32
+ const roles = inferRolesFromTags(['ci-cd']);
33
+ expect(roles).toContain('workflow');
34
+ });
35
+ it('infers security from security tags', () => {
36
+ const roles = inferRolesFromTags(['vulnerability']);
37
+ expect(roles).toContain('security');
38
+ });
39
+ it('infers development-partner from AI tags', () => {
40
+ const roles = inferRolesFromTags(['copilot']);
41
+ expect(roles).toContain('development-partner');
42
+ });
43
+ it('deduplicates roles from multiple matching tags', () => {
44
+ const roles = inferRolesFromTags(['lint', 'eslint', 'format']);
45
+ expect(roles).toEqual(['code-quality']);
46
+ });
47
+ it('returns multiple roles for diverse tags', () => {
48
+ const roles = inferRolesFromTags(['lint', 'testing', 'deploy']);
49
+ expect(roles).toContain('code-quality');
50
+ expect(roles).toContain('testing');
51
+ expect(roles).toContain('workflow');
52
+ expect(roles).toHaveLength(3);
53
+ });
54
+ it('normalizes hyphens and underscores in tags', () => {
55
+ const roles = inferRolesFromTags(['code_review']);
56
+ expect(roles).toContain('code-quality');
57
+ });
58
+ it('is case-insensitive', () => {
59
+ const roles = inferRolesFromTags(['LINT', 'Testing']);
60
+ expect(roles).toContain('code-quality');
61
+ expect(roles).toContain('testing');
62
+ });
63
+ });
64
+ // ============================================================================
65
+ // normalizeSkillName
66
+ // ============================================================================
67
+ describe('normalizeSkillName', () => {
68
+ it('lowercases the name', () => {
69
+ expect(normalizeSkillName('MyLinter')).toBe('mylinter');
70
+ });
71
+ it('removes hyphens and underscores', () => {
72
+ expect(normalizeSkillName('my-linter_tool')).toBe('mylintertool');
73
+ });
74
+ it('strips leading "skill" prefix', () => {
75
+ expect(normalizeSkillName('skill-formatter')).toBe('formatter');
76
+ });
77
+ it('strips trailing "skill" suffix', () => {
78
+ expect(normalizeSkillName('lint-skill')).toBe('lint');
79
+ });
80
+ it('strips leading "helper" prefix', () => {
81
+ expect(normalizeSkillName('helper-utils')).toBe('utils');
82
+ });
83
+ it('strips trailing "helper" suffix', () => {
84
+ expect(normalizeSkillName('code-helper')).toBe('code');
85
+ });
86
+ it('trims whitespace', () => {
87
+ expect(normalizeSkillName(' linter ')).toBe('linter');
88
+ });
89
+ it('handles combined normalization', () => {
90
+ expect(normalizeSkillName('Skill-Code_Helper')).toBe('code');
91
+ });
92
+ });
93
+ // ============================================================================
94
+ // skillsOverlap
95
+ // ============================================================================
96
+ describe('skillsOverlap', () => {
97
+ const makeInstalled = (name, tags = []) => ({
98
+ name,
99
+ directory: name,
100
+ tags,
101
+ category: null,
102
+ });
103
+ const makeRecommended = (name, skillId) => ({
104
+ skill_id: skillId,
105
+ name,
106
+ reason: 'test',
107
+ similarity_score: 0.8,
108
+ trust_tier: 'community',
109
+ quality_score: 80,
110
+ });
111
+ it('detects exact name match after normalization', () => {
112
+ const installed = makeInstalled('lint-skill');
113
+ const recommended = makeRecommended('skill-lint', 'author/skill-lint');
114
+ expect(skillsOverlap(installed, recommended)).toBe(true);
115
+ });
116
+ it('detects when skill_id contains installed name', () => {
117
+ const installed = makeInstalled('formatter');
118
+ const recommended = makeRecommended('code-formatter-pro', 'acme/formatter-pro');
119
+ expect(skillsOverlap(installed, recommended)).toBe(true);
120
+ });
121
+ it('detects substring overlap for names >= 4 chars', () => {
122
+ const installed = makeInstalled('linter');
123
+ const recommended = makeRecommended('super-linter', 'acme/super-linter');
124
+ expect(skillsOverlap(installed, recommended)).toBe(true);
125
+ });
126
+ it('does not detect substring overlap for short names', () => {
127
+ // Short names (<4 chars) skip substring check, but skill_id check still applies
128
+ // Use a skill_id that does NOT contain the installed name
129
+ const installed = makeInstalled('go');
130
+ const recommended = makeRecommended('rust-tools', 'acme/rust-tools');
131
+ expect(skillsOverlap(installed, recommended)).toBe(false);
132
+ });
133
+ it('detects tag overlap with recommended name parts', () => {
134
+ const installed = makeInstalled('my-tool', ['testing', 'vitest']);
135
+ const recommended = makeRecommended('vitest-helper', 'acme/vitest-helper');
136
+ expect(skillsOverlap(installed, recommended)).toBe(true);
137
+ });
138
+ it('returns false for unrelated skills', () => {
139
+ const installed = makeInstalled('formatter', ['formatting']);
140
+ const recommended = makeRecommended('security-auditor', 'acme/security-auditor');
141
+ expect(skillsOverlap(installed, recommended)).toBe(false);
142
+ });
143
+ });
144
+ // ============================================================================
145
+ // filterOverlappingSkills
146
+ // ============================================================================
147
+ describe('filterOverlappingSkills', () => {
148
+ const makeInstalled = (name, tags = []) => ({
149
+ name,
150
+ directory: name,
151
+ tags,
152
+ category: null,
153
+ });
154
+ const makeRecommended = (name, skillId) => ({
155
+ skill_id: skillId,
156
+ name,
157
+ reason: 'test',
158
+ similarity_score: 0.8,
159
+ trust_tier: 'community',
160
+ quality_score: 80,
161
+ });
162
+ it('returns all recommendations when no installed skills', () => {
163
+ const recs = [makeRecommended('foo', 'a/foo'), makeRecommended('bar', 'a/bar')];
164
+ const result = filterOverlappingSkills(recs, []);
165
+ expect(result.filtered).toHaveLength(2);
166
+ expect(result.overlapCount).toBe(0);
167
+ });
168
+ it('filters overlapping recommendations', () => {
169
+ const recs = [makeRecommended('linter', 'a/linter'), makeRecommended('security', 'a/security')];
170
+ const installed = [makeInstalled('linter')];
171
+ const result = filterOverlappingSkills(recs, installed);
172
+ expect(result.filtered).toHaveLength(1);
173
+ expect(result.filtered[0].name).toBe('security');
174
+ expect(result.overlapCount).toBe(1);
175
+ });
176
+ it('returns empty array when all overlap', () => {
177
+ const recs = [makeRecommended('linter', 'a/linter')];
178
+ const installed = [makeInstalled('linter')];
179
+ const result = filterOverlappingSkills(recs, installed);
180
+ expect(result.filtered).toHaveLength(0);
181
+ expect(result.overlapCount).toBe(1);
182
+ });
183
+ });
184
+ //# sourceMappingURL=recommend-scoring.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommend-scoring.test.js","sourceRoot":"","sources":["../../tests/recommend-scoring.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Unit tests for tool-analyzer.ts pure logic functions
3
+ *
4
+ * Tests analyzeToolRequirements, formatToolList, parseToolsString, validateTools.
5
+ * No mocking required — all functions are pure.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=tool-analyzer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-analyzer.test.d.ts","sourceRoot":"","sources":["../../tests/tool-analyzer.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}