developer-ai 1.0.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.
Files changed (105) hide show
  1. package/README.md +241 -0
  2. package/bin/developer-ai.js +2 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +219 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config/index.d.ts +7 -0
  8. package/dist/config/index.d.ts.map +1 -0
  9. package/dist/config/index.js +82 -0
  10. package/dist/config/index.js.map +1 -0
  11. package/dist/config/schema.d.ts +115 -0
  12. package/dist/config/schema.d.ts.map +1 -0
  13. package/dist/config/schema.js +29 -0
  14. package/dist/config/schema.js.map +1 -0
  15. package/dist/constants.d.ts +8 -0
  16. package/dist/constants.d.ts.map +1 -0
  17. package/dist/constants.js +8 -0
  18. package/dist/constants.js.map +1 -0
  19. package/dist/core/agent.d.ts +38 -0
  20. package/dist/core/agent.d.ts.map +1 -0
  21. package/dist/core/agent.js +155 -0
  22. package/dist/core/agent.js.map +1 -0
  23. package/dist/core/system-prompt.d.ts +6 -0
  24. package/dist/core/system-prompt.d.ts.map +1 -0
  25. package/dist/core/system-prompt.js +44 -0
  26. package/dist/core/system-prompt.js.map +1 -0
  27. package/dist/core/types.d.ts +42 -0
  28. package/dist/core/types.d.ts.map +1 -0
  29. package/dist/core/types.js +6 -0
  30. package/dist/core/types.js.map +1 -0
  31. package/dist/index.d.ts +15 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +12 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/mcp/client.d.ts +13 -0
  36. package/dist/mcp/client.d.ts.map +1 -0
  37. package/dist/mcp/client.js +202 -0
  38. package/dist/mcp/client.js.map +1 -0
  39. package/dist/providers/ollama.d.ts +13 -0
  40. package/dist/providers/ollama.d.ts.map +1 -0
  41. package/dist/providers/ollama.js +60 -0
  42. package/dist/providers/ollama.js.map +1 -0
  43. package/dist/providers/openai.d.ts +9 -0
  44. package/dist/providers/openai.d.ts.map +1 -0
  45. package/dist/providers/openai.js +40 -0
  46. package/dist/providers/openai.js.map +1 -0
  47. package/dist/skills/loader.d.ts +25 -0
  48. package/dist/skills/loader.d.ts.map +1 -0
  49. package/dist/skills/loader.js +93 -0
  50. package/dist/skills/loader.js.map +1 -0
  51. package/dist/tests/tools.test.d.ts +2 -0
  52. package/dist/tests/tools.test.d.ts.map +1 -0
  53. package/dist/tests/tools.test.js +170 -0
  54. package/dist/tests/tools.test.js.map +1 -0
  55. package/dist/tools/index.d.ts +5 -0
  56. package/dist/tools/index.d.ts.map +1 -0
  57. package/dist/tools/index.js +19 -0
  58. package/dist/tools/index.js.map +1 -0
  59. package/dist/tools/list-files.d.ts +3 -0
  60. package/dist/tools/list-files.d.ts.map +1 -0
  61. package/dist/tools/list-files.js +60 -0
  62. package/dist/tools/list-files.js.map +1 -0
  63. package/dist/tools/read-file.d.ts +3 -0
  64. package/dist/tools/read-file.d.ts.map +1 -0
  65. package/dist/tools/read-file.js +46 -0
  66. package/dist/tools/read-file.js.map +1 -0
  67. package/dist/tools/registry.d.ts +24 -0
  68. package/dist/tools/registry.d.ts.map +1 -0
  69. package/dist/tools/registry.js +37 -0
  70. package/dist/tools/registry.js.map +1 -0
  71. package/dist/tools/run-command.d.ts +3 -0
  72. package/dist/tools/run-command.d.ts.map +1 -0
  73. package/dist/tools/run-command.js +114 -0
  74. package/dist/tools/run-command.js.map +1 -0
  75. package/dist/tools/search-text.d.ts +3 -0
  76. package/dist/tools/search-text.d.ts.map +1 -0
  77. package/dist/tools/search-text.js +103 -0
  78. package/dist/tools/search-text.js.map +1 -0
  79. package/dist/tools/utils.d.ts +6 -0
  80. package/dist/tools/utils.d.ts.map +1 -0
  81. package/dist/tools/utils.js +14 -0
  82. package/dist/tools/utils.js.map +1 -0
  83. package/dist/tools/web-search.d.ts +3 -0
  84. package/dist/tools/web-search.d.ts.map +1 -0
  85. package/dist/tools/web-search.js +80 -0
  86. package/dist/tools/web-search.js.map +1 -0
  87. package/dist/tools/write-file.d.ts +3 -0
  88. package/dist/tools/write-file.d.ts.map +1 -0
  89. package/dist/tools/write-file.js +66 -0
  90. package/dist/tools/write-file.js.map +1 -0
  91. package/package.json +54 -0
  92. package/skills/accessibility/SKILL.md +496 -0
  93. package/skills/api-design/SKILL.md +419 -0
  94. package/skills/code-review/SKILL.md +267 -0
  95. package/skills/debugging/SKILL.md +332 -0
  96. package/skills/documentation/SKILL.md +496 -0
  97. package/skills/error-handling/SKILL.md +504 -0
  98. package/skills/git-workflow/SKILL.md +448 -0
  99. package/skills/human-like-coding/SKILL.md +400 -0
  100. package/skills/performance-optimization/SKILL.md +412 -0
  101. package/skills/prompt-engineering/SKILL.md +362 -0
  102. package/skills/refactoring/SKILL.md +457 -0
  103. package/skills/security-audit/SKILL.md +453 -0
  104. package/skills/testing-strategy/SKILL.md +501 -0
  105. package/skills/webapp-testing/SKILL.md +309 -0
@@ -0,0 +1,501 @@
1
+ ---
2
+ name: testing-strategy
3
+ description: Guide for developing comprehensive testing strategies. Use when planning test coverage, choosing testing frameworks, or establishing testing practices. Covers testing pyramid, unit/integration/E2E tests, TDD, and test organization.
4
+ ---
5
+
6
+ # Testing Strategy Skill
7
+
8
+ This skill provides guidance for developing effective testing strategies and writing maintainable tests.
9
+
10
+ ## Overview
11
+
12
+ A good testing strategy balances confidence, speed, and maintainability. This skill covers the testing pyramid, different test types, and best practices.
13
+
14
+ ## Testing Pyramid
15
+
16
+ ```
17
+ /\
18
+ / \ E2E Tests
19
+ /----\ (Few, slow, high confidence)
20
+ / \
21
+ /--------\ Integration Tests
22
+ / \ (Some, medium speed)
23
+ /------------\
24
+ / \ Unit Tests
25
+ /________________\ (Many, fast, focused)
26
+ ```
27
+
28
+ ### Test Distribution
29
+
30
+ | Type | Coverage | Speed | Quantity |
31
+ |------|----------|-------|----------|
32
+ | Unit | Single function/component | Fast (ms) | ~70% |
33
+ | Integration | Multiple modules | Medium (s) | ~20% |
34
+ | E2E | Full user flows | Slow (s-min) | ~10% |
35
+
36
+ ## Unit Testing
37
+
38
+ ### Structure (AAA Pattern)
39
+
40
+ ```javascript
41
+ describe('calculateDiscount', () => {
42
+ it('applies 10% discount for orders over $100', () => {
43
+ // Arrange
44
+ const order = { subtotal: 150 };
45
+
46
+ // Act
47
+ const discount = calculateDiscount(order);
48
+
49
+ // Assert
50
+ expect(discount).toBe(15);
51
+ });
52
+ });
53
+ ```
54
+
55
+ ### Naming Conventions
56
+
57
+ ```javascript
58
+ // Pattern: "should [expected behavior] when [condition]"
59
+ describe('UserService', () => {
60
+ describe('createUser', () => {
61
+ it('should create user when valid data provided', () => {});
62
+ it('should throw ValidationError when email is invalid', () => {});
63
+ it('should throw ConflictError when email already exists', () => {});
64
+ });
65
+ });
66
+ ```
67
+
68
+ ### Testing Pure Functions
69
+
70
+ ```javascript
71
+ // Pure functions are easiest to test
72
+ function sum(numbers) {
73
+ return numbers.reduce((a, b) => a + b, 0);
74
+ }
75
+
76
+ describe('sum', () => {
77
+ it('returns 0 for empty array', () => {
78
+ expect(sum([])).toBe(0);
79
+ });
80
+
81
+ it('returns the number for single-element array', () => {
82
+ expect(sum([5])).toBe(5);
83
+ });
84
+
85
+ it('sums all numbers in array', () => {
86
+ expect(sum([1, 2, 3, 4])).toBe(10);
87
+ });
88
+
89
+ it('handles negative numbers', () => {
90
+ expect(sum([1, -2, 3])).toBe(2);
91
+ });
92
+ });
93
+ ```
94
+
95
+ ### Testing with Mocks
96
+
97
+ ```javascript
98
+ import { vi } from 'vitest';
99
+
100
+ // Mock external dependencies
101
+ const mockDatabase = {
102
+ findUser: vi.fn(),
103
+ saveUser: vi.fn(),
104
+ };
105
+
106
+ describe('UserService', () => {
107
+ beforeEach(() => {
108
+ vi.clearAllMocks();
109
+ });
110
+
111
+ it('creates user in database', async () => {
112
+ mockDatabase.saveUser.mockResolvedValue({ id: '123', name: 'John' });
113
+
114
+ const service = new UserService(mockDatabase);
115
+ const user = await service.createUser({ name: 'John' });
116
+
117
+ expect(mockDatabase.saveUser).toHaveBeenCalledWith({ name: 'John' });
118
+ expect(user.id).toBe('123');
119
+ });
120
+
121
+ it('throws when database fails', async () => {
122
+ mockDatabase.saveUser.mockRejectedValue(new Error('DB Error'));
123
+
124
+ const service = new UserService(mockDatabase);
125
+
126
+ await expect(service.createUser({ name: 'John' }))
127
+ .rejects.toThrow('DB Error');
128
+ });
129
+ });
130
+ ```
131
+
132
+ ## Integration Testing
133
+
134
+ ### API Integration Tests
135
+
136
+ ```javascript
137
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
138
+ import request from 'supertest';
139
+ import app from './app';
140
+
141
+ describe('POST /api/users', () => {
142
+ beforeAll(async () => {
143
+ await setupTestDatabase();
144
+ });
145
+
146
+ afterAll(async () => {
147
+ await cleanupTestDatabase();
148
+ });
149
+
150
+ it('creates a new user', async () => {
151
+ const response = await request(app)
152
+ .post('/api/users')
153
+ .send({ name: 'John', email: 'john@example.com' })
154
+ .expect(201);
155
+
156
+ expect(response.body).toMatchObject({
157
+ name: 'John',
158
+ email: 'john@example.com',
159
+ });
160
+ expect(response.body.id).toBeDefined();
161
+ });
162
+
163
+ it('returns 400 for invalid email', async () => {
164
+ const response = await request(app)
165
+ .post('/api/users')
166
+ .send({ name: 'John', email: 'invalid' })
167
+ .expect(400);
168
+
169
+ expect(response.body.error.code).toBe('VALIDATION_ERROR');
170
+ });
171
+
172
+ it('returns 409 for duplicate email', async () => {
173
+ // First request succeeds
174
+ await request(app)
175
+ .post('/api/users')
176
+ .send({ name: 'John', email: 'duplicate@example.com' })
177
+ .expect(201);
178
+
179
+ // Second request with same email fails
180
+ const response = await request(app)
181
+ .post('/api/users')
182
+ .send({ name: 'Jane', email: 'duplicate@example.com' })
183
+ .expect(409);
184
+
185
+ expect(response.body.error.code).toBe('CONFLICT');
186
+ });
187
+ });
188
+ ```
189
+
190
+ ### Database Integration Tests
191
+
192
+ ```javascript
193
+ import { PrismaClient } from '@prisma/client';
194
+
195
+ const prisma = new PrismaClient();
196
+
197
+ describe('UserRepository', () => {
198
+ beforeEach(async () => {
199
+ // Clean database before each test
200
+ await prisma.user.deleteMany();
201
+ });
202
+
203
+ afterAll(async () => {
204
+ await prisma.$disconnect();
205
+ });
206
+
207
+ it('creates and retrieves user', async () => {
208
+ const created = await prisma.user.create({
209
+ data: { name: 'John', email: 'john@example.com' },
210
+ });
211
+
212
+ const found = await prisma.user.findUnique({
213
+ where: { id: created.id },
214
+ });
215
+
216
+ expect(found).toEqual(created);
217
+ });
218
+ });
219
+ ```
220
+
221
+ ## React Component Testing
222
+
223
+ ### Component Unit Tests
224
+
225
+ ```javascript
226
+ import { render, screen, fireEvent } from '@testing-library/react';
227
+ import { describe, it, expect, vi } from 'vitest';
228
+ import Button from './Button';
229
+
230
+ describe('Button', () => {
231
+ it('renders children', () => {
232
+ render(<Button>Click me</Button>);
233
+
234
+ expect(screen.getByRole('button')).toHaveTextContent('Click me');
235
+ });
236
+
237
+ it('calls onClick when clicked', () => {
238
+ const handleClick = vi.fn();
239
+ render(<Button onClick={handleClick}>Click me</Button>);
240
+
241
+ fireEvent.click(screen.getByRole('button'));
242
+
243
+ expect(handleClick).toHaveBeenCalledTimes(1);
244
+ });
245
+
246
+ it('is disabled when disabled prop is true', () => {
247
+ render(<Button disabled>Click me</Button>);
248
+
249
+ expect(screen.getByRole('button')).toBeDisabled();
250
+ });
251
+ });
252
+ ```
253
+
254
+ ### Testing with User Events
255
+
256
+ ```javascript
257
+ import { render, screen } from '@testing-library/react';
258
+ import userEvent from '@testing-library/user-event';
259
+ import LoginForm from './LoginForm';
260
+
261
+ describe('LoginForm', () => {
262
+ it('submits form with entered values', async () => {
263
+ const user = userEvent.setup();
264
+ const handleSubmit = vi.fn();
265
+
266
+ render(<LoginForm onSubmit={handleSubmit} />);
267
+
268
+ await user.type(screen.getByLabelText(/email/i), 'john@example.com');
269
+ await user.type(screen.getByLabelText(/password/i), 'password123');
270
+ await user.click(screen.getByRole('button', { name: /submit/i }));
271
+
272
+ expect(handleSubmit).toHaveBeenCalledWith({
273
+ email: 'john@example.com',
274
+ password: 'password123',
275
+ });
276
+ });
277
+
278
+ it('shows error for invalid email', async () => {
279
+ const user = userEvent.setup();
280
+ render(<LoginForm onSubmit={() => {}} />);
281
+
282
+ await user.type(screen.getByLabelText(/email/i), 'invalid');
283
+ await user.click(screen.getByRole('button', { name: /submit/i }));
284
+
285
+ expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
286
+ });
287
+ });
288
+ ```
289
+
290
+ ### Testing Async Components
291
+
292
+ ```javascript
293
+ import { render, screen, waitFor } from '@testing-library/react';
294
+ import UserProfile from './UserProfile';
295
+ import { mockFetch } from './testUtils';
296
+
297
+ describe('UserProfile', () => {
298
+ it('displays user data after loading', async () => {
299
+ mockFetch({ name: 'John Doe', email: 'john@example.com' });
300
+
301
+ render(<UserProfile userId="123" />);
302
+
303
+ // Initially shows loading
304
+ expect(screen.getByText(/loading/i)).toBeInTheDocument();
305
+
306
+ // Wait for data to appear
307
+ await waitFor(() => {
308
+ expect(screen.getByText('John Doe')).toBeInTheDocument();
309
+ });
310
+
311
+ expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();
312
+ });
313
+
314
+ it('displays error message on failure', async () => {
315
+ mockFetch(null, { status: 500 });
316
+
317
+ render(<UserProfile userId="123" />);
318
+
319
+ await waitFor(() => {
320
+ expect(screen.getByText(/error/i)).toBeInTheDocument();
321
+ });
322
+ });
323
+ });
324
+ ```
325
+
326
+ ## E2E Testing with Playwright
327
+
328
+ ### Basic E2E Test
329
+
330
+ ```javascript
331
+ import { test, expect } from '@playwright/test';
332
+
333
+ test.describe('User Registration', () => {
334
+ test('registers a new user', async ({ page }) => {
335
+ await page.goto('/register');
336
+
337
+ await page.fill('[name="name"]', 'John Doe');
338
+ await page.fill('[name="email"]', 'john@example.com');
339
+ await page.fill('[name="password"]', 'SecurePass123!');
340
+
341
+ await page.click('button[type="submit"]');
342
+
343
+ // Should redirect to dashboard
344
+ await expect(page).toHaveURL('/dashboard');
345
+ await expect(page.locator('h1')).toContainText('Welcome, John');
346
+ });
347
+
348
+ test('shows validation errors', async ({ page }) => {
349
+ await page.goto('/register');
350
+
351
+ await page.fill('[name="email"]', 'invalid');
352
+ await page.click('button[type="submit"]');
353
+
354
+ await expect(page.locator('.error')).toContainText('Invalid email');
355
+ });
356
+ });
357
+ ```
358
+
359
+ ### Page Object Pattern
360
+
361
+ ```javascript
362
+ // pages/LoginPage.js
363
+ export class LoginPage {
364
+ constructor(page) {
365
+ this.page = page;
366
+ this.emailInput = page.locator('[name="email"]');
367
+ this.passwordInput = page.locator('[name="password"]');
368
+ this.submitButton = page.locator('button[type="submit"]');
369
+ this.errorMessage = page.locator('.error-message');
370
+ }
371
+
372
+ async goto() {
373
+ await this.page.goto('/login');
374
+ }
375
+
376
+ async login(email, password) {
377
+ await this.emailInput.fill(email);
378
+ await this.passwordInput.fill(password);
379
+ await this.submitButton.click();
380
+ }
381
+ }
382
+
383
+ // tests/login.spec.js
384
+ import { test, expect } from '@playwright/test';
385
+ import { LoginPage } from '../pages/LoginPage';
386
+
387
+ test('successful login', async ({ page }) => {
388
+ const loginPage = new LoginPage(page);
389
+ await loginPage.goto();
390
+ await loginPage.login('user@example.com', 'password');
391
+
392
+ await expect(page).toHaveURL('/dashboard');
393
+ });
394
+ ```
395
+
396
+ ## Test Organization
397
+
398
+ ### File Structure
399
+
400
+ ```
401
+ src/
402
+ ├── components/
403
+ │ ├── Button/
404
+ │ │ ├── Button.tsx
405
+ │ │ ├── Button.test.tsx
406
+ │ │ └── Button.stories.tsx
407
+ │ └── Form/
408
+ │ ├── Form.tsx
409
+ │ └── Form.test.tsx
410
+ ├── services/
411
+ │ ├── userService.ts
412
+ │ └── userService.test.ts
413
+ └── utils/
414
+ ├── validation.ts
415
+ └── validation.test.ts
416
+
417
+ tests/
418
+ ├── integration/
419
+ │ └── api.test.ts
420
+ └── e2e/
421
+ ├── fixtures/
422
+ ├── pages/
423
+ └── specs/
424
+ ```
425
+
426
+ ### Test Setup
427
+
428
+ ```javascript
429
+ // vitest.config.ts
430
+ import { defineConfig } from 'vitest/config';
431
+
432
+ export default defineConfig({
433
+ test: {
434
+ globals: true,
435
+ environment: 'jsdom',
436
+ setupFiles: ['./tests/setup.ts'],
437
+ coverage: {
438
+ reporter: ['text', 'html'],
439
+ exclude: ['node_modules/', 'tests/'],
440
+ },
441
+ },
442
+ });
443
+ ```
444
+
445
+ ```javascript
446
+ // tests/setup.ts
447
+ import '@testing-library/jest-dom';
448
+ import { beforeAll, afterAll, afterEach } from 'vitest';
449
+ import { server } from './mocks/server';
450
+
451
+ beforeAll(() => server.listen());
452
+ afterEach(() => server.resetHandlers());
453
+ afterAll(() => server.close());
454
+ ```
455
+
456
+ ## TDD Workflow
457
+
458
+ ### Red-Green-Refactor
459
+
460
+ ```javascript
461
+ // 1. RED: Write failing test
462
+ it('returns true for valid email', () => {
463
+ expect(isValidEmail('test@example.com')).toBe(true);
464
+ });
465
+
466
+ // 2. GREEN: Implement minimal code to pass
467
+ function isValidEmail(email) {
468
+ return email.includes('@');
469
+ }
470
+
471
+ // 3. REFACTOR: Improve implementation
472
+ function isValidEmail(email) {
473
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
474
+ return emailRegex.test(email);
475
+ }
476
+ ```
477
+
478
+ ## Testing Checklist
479
+
480
+ ### Unit Tests
481
+ - [ ] Pure functions tested
482
+ - [ ] Edge cases covered
483
+ - [ ] Error scenarios tested
484
+ - [ ] Mocks used appropriately
485
+
486
+ ### Integration Tests
487
+ - [ ] API endpoints tested
488
+ - [ ] Database operations verified
489
+ - [ ] External services mocked
490
+
491
+ ### E2E Tests
492
+ - [ ] Critical user flows covered
493
+ - [ ] Cross-browser testing
494
+ - [ ] Mobile viewports tested
495
+ - [ ] Accessibility checked
496
+
497
+ ### General
498
+ - [ ] Naming conventions followed
499
+ - [ ] Tests are independent
500
+ - [ ] Setup/teardown implemented
501
+ - [ ] Coverage targets met