@skillsmith/cli 0.5.9 → 0.5.11

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.
@@ -0,0 +1,235 @@
1
+ /**
2
+ * SMI-1353: CLI recommend command — stack building, role-based filtering,
3
+ * module exports.
4
+ *
5
+ * Sibling of recommend.test.ts and recommend.errors.test.ts; see those files
6
+ * for context. Shared mocks/fixtures live in recommend.test-helpers.
7
+ */
8
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
9
+ import { createMockCodebaseContext, createMockApiResponse } from './recommend.test-helpers.js';
10
+ const mocks = vi.hoisted(() => ({
11
+ analyze: vi.fn(),
12
+ getRecommendations: vi.fn(),
13
+ spinner: {
14
+ start: vi.fn().mockReturnThis(),
15
+ stop: vi.fn().mockReturnThis(),
16
+ succeed: vi.fn().mockReturnThis(),
17
+ fail: vi.fn().mockReturnThis(),
18
+ warn: vi.fn().mockReturnThis(),
19
+ text: '',
20
+ },
21
+ }));
22
+ vi.mock('@skillsmith/core', () => ({
23
+ CodebaseAnalyzer: class MockCodebaseAnalyzer {
24
+ analyze(...args) {
25
+ return mocks.analyze(...args);
26
+ }
27
+ },
28
+ createApiClient: () => ({
29
+ getRecommendations: (...args) => mocks.getRecommendations(...args),
30
+ }),
31
+ // SMI-4474: stub for JWT auto-load (tests don't exercise the JWT path).
32
+ loadStoredAccessToken: () => Promise.resolve(null),
33
+ SKILL_ROLES: [
34
+ 'code-quality',
35
+ 'testing',
36
+ 'documentation',
37
+ 'workflow',
38
+ 'security',
39
+ 'development-partner',
40
+ ],
41
+ }));
42
+ vi.mock('ora', () => ({ default: () => mocks.spinner }));
43
+ const mockAnalyze = mocks.analyze;
44
+ const mockGetRecommendations = mocks.getRecommendations;
45
+ const originalConsoleLog = console.log;
46
+ const originalConsoleError = console.error;
47
+ const mockConsoleLog = vi.fn();
48
+ const mockConsoleError = vi.fn();
49
+ vi.spyOn(process, 'exit').mockImplementation(() => undefined);
50
+ describe('SMI-1353: CLI recommend command — stack / role filter / exports', () => {
51
+ beforeEach(() => {
52
+ vi.clearAllMocks();
53
+ console.log = mockConsoleLog;
54
+ console.error = mockConsoleError;
55
+ mockAnalyze.mockResolvedValue(createMockCodebaseContext());
56
+ mockGetRecommendations.mockResolvedValue(createMockApiResponse());
57
+ });
58
+ afterEach(() => {
59
+ console.log = originalConsoleLog;
60
+ console.error = originalConsoleError;
61
+ });
62
+ // ==========================================================================
63
+ // Stack Building Tests
64
+ // ==========================================================================
65
+ describe('stack building from analysis', () => {
66
+ it('should include framework names in stack (lowercase)', async () => {
67
+ mockAnalyze.mockResolvedValue(createMockCodebaseContext({
68
+ frameworks: [
69
+ { name: 'Next.js', confidence: 0.9, source: 'dep', detectedFrom: [] },
70
+ { name: 'TailwindCSS', confidence: 0.85, source: 'dep', detectedFrom: [] },
71
+ ],
72
+ dependencies: [{ name: 'next', version: '^14.0.0', isDev: false }],
73
+ }));
74
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
75
+ const cmd = createRecommendCommand();
76
+ await cmd.parseAsync(['node', 'test', '.']);
77
+ expect(mockGetRecommendations).toHaveBeenCalledWith(expect.objectContaining({
78
+ stack: expect.arrayContaining(['next.js', 'tailwindcss']),
79
+ }));
80
+ });
81
+ it('should include non-dev dependencies in stack', async () => {
82
+ mockAnalyze.mockResolvedValue(createMockCodebaseContext({
83
+ frameworks: [{ name: 'Express', confidence: 0.9, source: 'dep', detectedFrom: [] }],
84
+ dependencies: [
85
+ { name: 'express', version: '^4.0.0', isDev: false },
86
+ { name: 'mongoose', version: '^7.0.0', isDev: false },
87
+ ],
88
+ }));
89
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
90
+ const cmd = createRecommendCommand();
91
+ await cmd.parseAsync(['node', 'test', '.']);
92
+ expect(mockGetRecommendations).toHaveBeenCalledWith(expect.objectContaining({
93
+ stack: expect.arrayContaining(['express', 'mongoose']),
94
+ }));
95
+ });
96
+ it('should exclude dev dependencies from stack', async () => {
97
+ mockAnalyze.mockResolvedValue(createMockCodebaseContext({
98
+ frameworks: [{ name: 'React', confidence: 0.9, source: 'dep', detectedFrom: [] }],
99
+ dependencies: [
100
+ { name: 'react', version: '^18.0.0', isDev: false },
101
+ { name: 'jest', version: '^29.0.0', isDev: true },
102
+ { name: 'eslint', version: '^8.0.0', isDev: true },
103
+ ],
104
+ }));
105
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
106
+ const cmd = createRecommendCommand();
107
+ await cmd.parseAsync(['node', 'test', '.']);
108
+ const call = mockGetRecommendations.mock.calls[0][0];
109
+ expect(call.stack).not.toContain('jest');
110
+ expect(call.stack).not.toContain('eslint');
111
+ expect(call.stack).toContain('react');
112
+ });
113
+ it('should limit stack to 10 items', async () => {
114
+ mockAnalyze.mockResolvedValue(createMockCodebaseContext({
115
+ frameworks: Array.from({ length: 6 }, (_, i) => ({
116
+ name: `Framework${i}`,
117
+ confidence: 0.9,
118
+ source: 'dep',
119
+ detectedFrom: [],
120
+ })),
121
+ dependencies: Array.from({ length: 12 }, (_, i) => ({
122
+ name: `dep${i}`,
123
+ version: '^1.0.0',
124
+ isDev: false,
125
+ })),
126
+ }));
127
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
128
+ const cmd = createRecommendCommand();
129
+ await cmd.parseAsync(['node', 'test', '.']);
130
+ const call = mockGetRecommendations.mock.calls[0][0];
131
+ expect(call.stack.length).toBeLessThanOrEqual(10);
132
+ });
133
+ it('should deduplicate stack items', async () => {
134
+ mockAnalyze.mockResolvedValue(createMockCodebaseContext({
135
+ frameworks: [{ name: 'React', confidence: 0.9, source: 'dep', detectedFrom: [] }],
136
+ dependencies: [{ name: 'react', version: '^18.0.0', isDev: false }],
137
+ }));
138
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
139
+ const cmd = createRecommendCommand();
140
+ await cmd.parseAsync(['node', 'test', '.']);
141
+ const call = mockGetRecommendations.mock.calls[0][0];
142
+ const reactCount = call.stack.filter((s) => s === 'react').length;
143
+ expect(reactCount).toBe(1);
144
+ });
145
+ });
146
+ // ==========================================================================
147
+ // SMI-1631: Role-Based Filtering Tests
148
+ // ==========================================================================
149
+ describe('role-based filtering', () => {
150
+ it('should apply role filtering locally', async () => {
151
+ mockGetRecommendations.mockResolvedValue(createMockApiResponse([
152
+ {
153
+ id: 'test/test-helper',
154
+ name: 'Test Helper',
155
+ description: 'Testing utilities',
156
+ author: 'test',
157
+ repo_url: null,
158
+ quality_score: 0.8,
159
+ trust_tier: 'verified',
160
+ tags: ['testing', 'jest', 'unit-test'],
161
+ stars: 100,
162
+ created_at: '2024-01-01',
163
+ updated_at: '2024-01-01',
164
+ },
165
+ ]));
166
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
167
+ const cmd = createRecommendCommand();
168
+ await cmd.parseAsync(['node', 'test', '.', '--role', 'testing', '--json']);
169
+ const output = mockConsoleLog.mock.calls[0][0];
170
+ const parsed = JSON.parse(output);
171
+ expect(parsed.meta.role_filter).toBe('testing');
172
+ });
173
+ it('should not set role filter for invalid role', async () => {
174
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
175
+ const cmd = createRecommendCommand();
176
+ await cmd.parseAsync(['node', 'test', '.', '--role', 'invalid-role', '--json']);
177
+ const output = mockConsoleLog.mock.calls[0][0];
178
+ const parsed = JSON.parse(output);
179
+ expect(parsed.meta.role_filter).toBeNull();
180
+ });
181
+ it('should warn user about invalid role', async () => {
182
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
183
+ const cmd = createRecommendCommand();
184
+ await cmd.parseAsync(['node', 'test', '.', '--role', 'not-a-role']);
185
+ const errorOutput = mockConsoleError.mock.calls.map((c) => c[0]).join('\n');
186
+ expect(errorOutput).toContain('Invalid role');
187
+ expect(errorOutput).toContain('not-a-role');
188
+ });
189
+ it('should accept all valid role values', async () => {
190
+ const validRoles = [
191
+ 'code-quality',
192
+ 'testing',
193
+ 'documentation',
194
+ 'workflow',
195
+ 'security',
196
+ 'development-partner',
197
+ ];
198
+ for (const role of validRoles) {
199
+ vi.clearAllMocks();
200
+ mockAnalyze.mockResolvedValue(createMockCodebaseContext());
201
+ mockGetRecommendations.mockResolvedValue(createMockApiResponse());
202
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
203
+ const cmd = createRecommendCommand();
204
+ await cmd.parseAsync(['node', 'test', '.', '-r', role, '--json']);
205
+ const output = mockConsoleLog.mock.calls[0][0];
206
+ const parsed = JSON.parse(output);
207
+ expect(parsed.meta.role_filter).toBe(role);
208
+ }
209
+ });
210
+ it('should include role_filter in JSON output when specified', async () => {
211
+ const { createRecommendCommand } = await import('../src/commands/recommend.js');
212
+ const cmd = createRecommendCommand();
213
+ await cmd.parseAsync(['node', 'test', '.', '--role', 'security', '--json']);
214
+ const output = mockConsoleLog.mock.calls[0][0];
215
+ const parsed = JSON.parse(output);
216
+ expect(parsed.meta.role_filter).toBe('security');
217
+ });
218
+ });
219
+ // ==========================================================================
220
+ // Export Tests
221
+ // ==========================================================================
222
+ describe('module exports', () => {
223
+ it('should export createRecommendCommand from commands/index', async () => {
224
+ const indexExports = await import('../src/commands/index.js');
225
+ expect(indexExports.createRecommendCommand).toBeDefined();
226
+ expect(typeof indexExports.createRecommendCommand).toBe('function');
227
+ });
228
+ it('should export createRecommendCommand as default', async () => {
229
+ const mod = await import('../src/commands/recommend.js');
230
+ expect(mod.default).toBeDefined();
231
+ expect(typeof mod.default).toBe('function');
232
+ });
233
+ });
234
+ });
235
+ //# sourceMappingURL=recommend.filters.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommend.filters.test.js","sourceRoot":"","sources":["../../tests/recommend.filters.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Shared fixtures for the SMI-1353 recommend command test files.
3
+ *
4
+ * The full suite was split across three sibling files (`recommend.test.ts`,
5
+ * `recommend.errors.test.ts`, `recommend.filters.test.ts`) so each file stays
6
+ * under the 500-line standard. Pure fixtures (no `vi.hoisted`, no mock
7
+ * factory) live here; each test file owns its own `vi.hoisted` mocks +
8
+ * `vi.mock(...)` block since vitest doesn't allow exporting hoisted values
9
+ * across module boundaries.
10
+ */
11
+ /**
12
+ * Skill data type for mock responses
13
+ */
14
+ export interface MockSkillData {
15
+ id: string;
16
+ name: string;
17
+ description: string;
18
+ author: string;
19
+ repo_url: string | null;
20
+ quality_score: number;
21
+ trust_tier: string;
22
+ tags: string[];
23
+ stars: number;
24
+ created_at: string;
25
+ updated_at: string;
26
+ }
27
+ /**
28
+ * Creates a mock CodebaseContext for testing
29
+ */
30
+ export declare function createMockCodebaseContext(overrides?: {}): {
31
+ rootPath: string;
32
+ imports: never[];
33
+ exports: never[];
34
+ functions: never[];
35
+ frameworks: {
36
+ name: string;
37
+ confidence: number;
38
+ source: string;
39
+ detectedFrom: never[];
40
+ }[];
41
+ dependencies: {
42
+ name: string;
43
+ version: string;
44
+ isDev: boolean;
45
+ }[];
46
+ stats: {
47
+ totalFiles: number;
48
+ filesByExtension: {
49
+ '.ts': number;
50
+ '.tsx': number;
51
+ };
52
+ totalLines: number;
53
+ };
54
+ metadata: {
55
+ durationMs: number;
56
+ version: string;
57
+ };
58
+ };
59
+ /**
60
+ * Creates a mock API response for recommendations
61
+ */
62
+ export declare function createMockApiResponse(skills?: MockSkillData[]): {
63
+ data: MockSkillData[];
64
+ meta: {};
65
+ };
66
+ //# sourceMappingURL=recommend.test-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommend.test-helpers.d.ts","sourceRoot":"","sources":["../../tests/recommend.test-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BvD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,GAAE,aAAa,EAAO;;;EAmCjE"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shared fixtures for the SMI-1353 recommend command test files.
3
+ *
4
+ * The full suite was split across three sibling files (`recommend.test.ts`,
5
+ * `recommend.errors.test.ts`, `recommend.filters.test.ts`) so each file stays
6
+ * under the 500-line standard. Pure fixtures (no `vi.hoisted`, no mock
7
+ * factory) live here; each test file owns its own `vi.hoisted` mocks +
8
+ * `vi.mock(...)` block since vitest doesn't allow exporting hoisted values
9
+ * across module boundaries.
10
+ */
11
+ /**
12
+ * Creates a mock CodebaseContext for testing
13
+ */
14
+ export function createMockCodebaseContext(overrides = {}) {
15
+ return {
16
+ rootPath: '/test/project',
17
+ imports: [],
18
+ exports: [],
19
+ functions: [],
20
+ frameworks: [
21
+ { name: 'React', confidence: 0.95, source: 'dep', detectedFrom: [] },
22
+ { name: 'TypeScript', confidence: 0.9, source: 'dep', detectedFrom: [] },
23
+ ],
24
+ dependencies: [
25
+ { name: 'react', version: '^18.0.0', isDev: false },
26
+ { name: 'typescript', version: '^5.0.0', isDev: true },
27
+ { name: 'jest', version: '^29.0.0', isDev: true },
28
+ ],
29
+ stats: {
30
+ totalFiles: 42,
31
+ filesByExtension: { '.ts': 30, '.tsx': 12 },
32
+ totalLines: 5000,
33
+ },
34
+ metadata: {
35
+ durationMs: 150,
36
+ version: '1.0.0',
37
+ },
38
+ ...overrides,
39
+ };
40
+ }
41
+ /**
42
+ * Creates a mock API response for recommendations
43
+ */
44
+ export function createMockApiResponse(skills = []) {
45
+ return {
46
+ data: skills.length > 0
47
+ ? skills
48
+ : [
49
+ {
50
+ id: 'anthropic/jest-helper',
51
+ name: 'Jest Helper',
52
+ description: 'Jest testing utilities',
53
+ author: 'anthropic',
54
+ repo_url: 'https://github.com/anthropic/jest-helper',
55
+ quality_score: 0.85,
56
+ trust_tier: 'verified',
57
+ tags: ['testing', 'jest'],
58
+ stars: 150,
59
+ created_at: '2024-01-01',
60
+ updated_at: '2024-01-15',
61
+ },
62
+ {
63
+ id: 'community/react-tools',
64
+ name: 'React Tools',
65
+ description: 'React development utilities',
66
+ author: 'community',
67
+ repo_url: 'https://github.com/community/react-tools',
68
+ quality_score: 0.72,
69
+ trust_tier: 'community',
70
+ tags: ['react', 'development'],
71
+ stars: 89,
72
+ created_at: '2024-02-01',
73
+ updated_at: '2024-02-10',
74
+ },
75
+ ],
76
+ meta: {},
77
+ };
78
+ }
79
+ //# sourceMappingURL=recommend.test-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommend.test-helpers.js","sourceRoot":"","sources":["../../tests/recommend.test-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAmBH;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,SAAS,GAAG,EAAE;IACtD,OAAO;QACL,QAAQ,EAAE,eAAe;QACzB,OAAO,EAAE,EAAE;QACX,OAAO,EAAE,EAAE;QACX,SAAS,EAAE,EAAE;QACb,UAAU,EAAE;YACV,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE;YACpE,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE;SACzE;QACD,YAAY,EAAE;YACZ,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE;YACnD,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE;YACtD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE;SAClD;QACD,KAAK,EAAE;YACL,UAAU,EAAE,EAAE;YACd,gBAAgB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;YAC3C,UAAU,EAAE,IAAI;SACjB;QACD,QAAQ,EAAE;YACR,UAAU,EAAE,GAAG;YACf,OAAO,EAAE,OAAO;SACjB;QACD,GAAG,SAAS;KACb,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAA0B,EAAE;IAChE,OAAO;QACL,IAAI,EACF,MAAM,CAAC,MAAM,GAAG,CAAC;YACf,CAAC,CAAC,MAAM;YACR,CAAC,CAAC;gBACE;oBACE,EAAE,EAAE,uBAAuB;oBAC3B,IAAI,EAAE,aAAa;oBACnB,WAAW,EAAE,wBAAwB;oBACrC,MAAM,EAAE,WAAW;oBACnB,QAAQ,EAAE,0CAA0C;oBACpD,aAAa,EAAE,IAAI;oBACnB,UAAU,EAAE,UAAU;oBACtB,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC;oBACzB,KAAK,EAAE,GAAG;oBACV,UAAU,EAAE,YAAY;oBACxB,UAAU,EAAE,YAAY;iBACzB;gBACD;oBACE,EAAE,EAAE,uBAAuB;oBAC3B,IAAI,EAAE,aAAa;oBACnB,WAAW,EAAE,6BAA6B;oBAC1C,MAAM,EAAE,WAAW;oBACnB,QAAQ,EAAE,0CAA0C;oBACpD,aAAa,EAAE,IAAI;oBACnB,UAAU,EAAE,WAAW;oBACvB,IAAI,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC;oBAC9B,KAAK,EAAE,EAAE;oBACT,UAAU,EAAE,YAAY;oBACxB,UAAU,EAAE,YAAY;iBACzB;aACF;QACP,IAAI,EAAE,EAAE;KACT,CAAA;AACH,CAAC"}
@@ -1,8 +1,9 @@
1
1
  /**
2
- * SMI-1353: CLI recommend command tests with real behavior assertions
2
+ * SMI-1353: CLI recommend command registration + analyzer + API + output.
3
3
  *
4
- * Tests follow London School TDD with mocked dependencies to verify
5
- * interactions between the recommend command and its collaborators.
4
+ * Split from the original 1012-line file into three sibling files (this one,
5
+ * recommend.errors.test.ts, recommend.filters.test.ts) so each stays under
6
+ * the 500-line standard. Shared mocks/fixtures live in recommend.test-helpers.
6
7
  *
7
8
  * Parent issue: SMI-1299 (CLI recommend command)
8
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"recommend.test.d.ts","sourceRoot":"","sources":["../../tests/recommend.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
1
+ {"version":3,"file":"recommend.test.d.ts","sourceRoot":"","sources":["../../tests/recommend.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}