@paths.design/caws-cli 3.2.4 → 3.3.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/commands/diagnose.d.ts +52 -0
- package/dist/commands/diagnose.d.ts.map +1 -0
- package/dist/commands/diagnose.js +472 -0
- package/dist/commands/status.d.ts +38 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +276 -0
- package/dist/commands/templates.d.ts +74 -0
- package/dist/commands/templates.d.ts.map +1 -0
- package/dist/commands/templates.js +235 -0
- package/dist/error-handler.d.ts +27 -2
- package/dist/error-handler.d.ts.map +1 -1
- package/dist/error-handler.js +187 -16
- package/dist/generators/jest-config.d.ts +32 -0
- package/dist/generators/jest-config.d.ts.map +1 -0
- package/dist/generators/jest-config.js +242 -0
- package/dist/index.js +174 -4
- package/dist/utils/typescript-detector.d.ts +32 -0
- package/dist/utils/typescript-detector.d.ts.map +1 -0
- package/dist/utils/typescript-detector.js +185 -0
- package/package.json +1 -1
package/dist/error-handler.js
CHANGED
|
@@ -137,19 +137,152 @@ async function safeAsync(operation, context = '') {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Command-specific error suggestions
|
|
142
|
+
*/
|
|
143
|
+
const COMMAND_SUGGESTIONS = {
|
|
144
|
+
'unknown option': (option, command) => {
|
|
145
|
+
const suggestions = [];
|
|
146
|
+
|
|
147
|
+
// Common typos and alternatives
|
|
148
|
+
const optionMap = {
|
|
149
|
+
'--suggestions': 'Validation includes suggestions by default. Try: caws validate',
|
|
150
|
+
'--suggest': 'Validation includes suggestions by default. Try: caws validate',
|
|
151
|
+
'--help': 'Try: caws --help or caws <command> --help',
|
|
152
|
+
'--json': 'For JSON output, try: caws provenance show --format=json',
|
|
153
|
+
'--dashboard': 'Try: caws provenance show --format=dashboard',
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if (optionMap[option]) {
|
|
157
|
+
suggestions.push(optionMap[option]);
|
|
158
|
+
} else {
|
|
159
|
+
suggestions.push(`Try: caws ${command || ''} --help for available options`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return suggestions;
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
'unknown command': (command) => {
|
|
166
|
+
const validCommands = [
|
|
167
|
+
'init',
|
|
168
|
+
'validate',
|
|
169
|
+
'scaffold',
|
|
170
|
+
'status',
|
|
171
|
+
'templates',
|
|
172
|
+
'provenance',
|
|
173
|
+
'hooks',
|
|
174
|
+
'burnup',
|
|
175
|
+
'tool',
|
|
176
|
+
];
|
|
177
|
+
const similar = findSimilarCommand(command, validCommands);
|
|
178
|
+
|
|
179
|
+
const suggestions = [];
|
|
180
|
+
if (similar) {
|
|
181
|
+
suggestions.push(`Did you mean: caws ${similar}?`);
|
|
182
|
+
}
|
|
183
|
+
suggestions.push(
|
|
184
|
+
'Available commands: init, validate, scaffold, status, templates, provenance, hooks'
|
|
185
|
+
);
|
|
186
|
+
suggestions.push('Try: caws --help for full command list');
|
|
187
|
+
|
|
188
|
+
return suggestions;
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
'template not found': () => [
|
|
192
|
+
'Templates are bundled with CAWS CLI',
|
|
193
|
+
'Try: caws scaffold (should work automatically)',
|
|
194
|
+
'If issue persists: npm i -g @paths.design/caws-cli@latest',
|
|
195
|
+
],
|
|
196
|
+
|
|
197
|
+
'not a caws project': () => [
|
|
198
|
+
'Initialize CAWS first: caws init .',
|
|
199
|
+
'Or create new project: caws init <project-name>',
|
|
200
|
+
'Check for .caws/working-spec.yaml file',
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Find similar command using Levenshtein distance
|
|
206
|
+
* @param {string} input - User's input command
|
|
207
|
+
* @param {string[]} validCommands - List of valid commands
|
|
208
|
+
* @returns {string|null} Most similar command or null
|
|
209
|
+
*/
|
|
210
|
+
function findSimilarCommand(input, validCommands) {
|
|
211
|
+
if (!input) return null;
|
|
212
|
+
|
|
213
|
+
let minDistance = Infinity;
|
|
214
|
+
let closestMatch = null;
|
|
215
|
+
|
|
216
|
+
for (const cmd of validCommands) {
|
|
217
|
+
const distance = levenshteinDistance(input.toLowerCase(), cmd.toLowerCase());
|
|
218
|
+
if (distance < minDistance && distance <= 2) {
|
|
219
|
+
minDistance = distance;
|
|
220
|
+
closestMatch = cmd;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return closestMatch;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Calculate Levenshtein distance between two strings
|
|
229
|
+
* @param {string} a - First string
|
|
230
|
+
* @param {string} b - Second string
|
|
231
|
+
* @returns {number} Edit distance
|
|
232
|
+
*/
|
|
233
|
+
function levenshteinDistance(a, b) {
|
|
234
|
+
const matrix = [];
|
|
235
|
+
|
|
236
|
+
for (let i = 0; i <= b.length; i++) {
|
|
237
|
+
matrix[i] = [i];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
for (let j = 0; j <= a.length; j++) {
|
|
241
|
+
matrix[0][j] = j;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
for (let i = 1; i <= b.length; i++) {
|
|
245
|
+
for (let j = 1; j <= a.length; j++) {
|
|
246
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
247
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
248
|
+
} else {
|
|
249
|
+
matrix[i][j] = Math.min(
|
|
250
|
+
matrix[i - 1][j - 1] + 1,
|
|
251
|
+
matrix[i][j - 1] + 1,
|
|
252
|
+
matrix[i - 1][j] + 1
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return matrix[b.length][a.length];
|
|
259
|
+
}
|
|
260
|
+
|
|
140
261
|
/**
|
|
141
262
|
* Get recovery suggestions based on error category
|
|
142
263
|
* @param {Error} error - Original error
|
|
143
264
|
* @param {string} category - Error category
|
|
265
|
+
* @param {Object} context - Additional context (command, options, etc.)
|
|
144
266
|
* @returns {string[]} Array of recovery suggestions
|
|
145
267
|
*/
|
|
146
|
-
function getRecoverySuggestions(error, category) {
|
|
268
|
+
function getRecoverySuggestions(error, category, context = {}) {
|
|
147
269
|
const suggestions = [];
|
|
270
|
+
const errorMessage = error.message || '';
|
|
271
|
+
|
|
272
|
+
// Check for command-specific suggestions first
|
|
273
|
+
for (const [pattern, suggestionFn] of Object.entries(COMMAND_SUGGESTIONS)) {
|
|
274
|
+
if (errorMessage.toLowerCase().includes(pattern)) {
|
|
275
|
+
const commandSuggestions = suggestionFn(context.option, context.command);
|
|
276
|
+
suggestions.push(...commandSuggestions);
|
|
277
|
+
return suggestions;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
148
280
|
|
|
281
|
+
// Fall back to category-based suggestions
|
|
149
282
|
switch (category) {
|
|
150
283
|
case ERROR_CATEGORIES.PERMISSION:
|
|
151
284
|
suggestions.push('Try running the command with elevated privileges (sudo)');
|
|
152
|
-
suggestions.push('Check file/directory permissions with
|
|
285
|
+
suggestions.push('Check file/directory permissions with: ls -la');
|
|
153
286
|
break;
|
|
154
287
|
|
|
155
288
|
case ERROR_CATEGORIES.FILESYSTEM:
|
|
@@ -163,52 +296,87 @@ function getRecoverySuggestions(error, category) {
|
|
|
163
296
|
break;
|
|
164
297
|
|
|
165
298
|
case ERROR_CATEGORIES.VALIDATION:
|
|
166
|
-
suggestions.push('Run
|
|
167
|
-
suggestions.push('Check your working spec format against the
|
|
299
|
+
suggestions.push('Run: caws validate for detailed validation help');
|
|
300
|
+
suggestions.push('Check your working spec format against the schema');
|
|
301
|
+
suggestions.push('See: docs/api/schema.md for specification details');
|
|
168
302
|
break;
|
|
169
303
|
|
|
170
304
|
case ERROR_CATEGORIES.CONFIGURATION:
|
|
171
|
-
suggestions.push('Run
|
|
305
|
+
suggestions.push('Run: caws init --interactive to reconfigure');
|
|
172
306
|
suggestions.push('Check your .caws directory and configuration files');
|
|
307
|
+
suggestions.push('Try: caws diagnose to identify configuration issues');
|
|
173
308
|
break;
|
|
174
309
|
|
|
175
310
|
case ERROR_CATEGORIES.NETWORK:
|
|
176
311
|
suggestions.push('Check your internet connection');
|
|
177
312
|
suggestions.push('Verify the URL/service is accessible');
|
|
313
|
+
suggestions.push('Try again in a few moments');
|
|
178
314
|
break;
|
|
179
315
|
|
|
180
316
|
default:
|
|
181
|
-
suggestions.push('Run
|
|
182
|
-
suggestions.push('
|
|
317
|
+
suggestions.push('Run: caws --help for usage information');
|
|
318
|
+
suggestions.push('See: docs/agents/full-guide.md for detailed documentation');
|
|
183
319
|
}
|
|
184
320
|
|
|
185
321
|
return suggestions;
|
|
186
322
|
}
|
|
187
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Get documentation link for error category
|
|
326
|
+
* @param {string} category - Error category
|
|
327
|
+
* @param {Object} context - Additional context
|
|
328
|
+
* @returns {string} Documentation URL
|
|
329
|
+
*/
|
|
330
|
+
function getDocumentationLink(category, context = {}) {
|
|
331
|
+
const baseUrl = 'https://github.com/Paths-Design/coding-agent-working-standard/blob/main';
|
|
332
|
+
|
|
333
|
+
const categoryLinks = {
|
|
334
|
+
validation: `${baseUrl}/docs/api/schema.md`,
|
|
335
|
+
configuration: `${baseUrl}/docs/guides/caws-developer-guide.md`,
|
|
336
|
+
filesystem: `${baseUrl}/docs/agents/tutorial.md`,
|
|
337
|
+
permission: `${baseUrl}/SECURITY.md`,
|
|
338
|
+
network: `${baseUrl}/README.md#requirements`,
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
if (context.command) {
|
|
342
|
+
const commandLinks = {
|
|
343
|
+
init: `${baseUrl}/docs/agents/tutorial.md#initialization`,
|
|
344
|
+
validate: `${baseUrl}/docs/api/cli.md#validate`,
|
|
345
|
+
scaffold: `${baseUrl}/docs/api/cli.md#scaffold`,
|
|
346
|
+
provenance: `${baseUrl}/docs/api/cli.md#provenance`,
|
|
347
|
+
hooks: `${baseUrl}/docs/guides/hooks-and-agent-workflows.md`,
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
if (commandLinks[context.command]) {
|
|
351
|
+
return commandLinks[context.command];
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return categoryLinks[category] || `${baseUrl}/docs/agents/full-guide.md`;
|
|
356
|
+
}
|
|
357
|
+
|
|
188
358
|
/**
|
|
189
359
|
* Handle CLI errors with consistent formatting and user guidance
|
|
190
360
|
* @param {Error} error - Error to handle
|
|
361
|
+
* @param {Object} context - Error context (command, option, etc.)
|
|
191
362
|
* @param {boolean} exit - Whether to exit the process (default: true)
|
|
192
363
|
*/
|
|
193
|
-
function handleCliError(error, exit = true) {
|
|
364
|
+
function handleCliError(error, context = {}, exit = true) {
|
|
194
365
|
const category = error.category || getErrorCategory(error);
|
|
195
|
-
const suggestions = error.suggestions || getRecoverySuggestions(error, category);
|
|
366
|
+
const suggestions = error.suggestions || getRecoverySuggestions(error, category, context);
|
|
367
|
+
const docLink = getDocumentationLink(category, context);
|
|
196
368
|
|
|
197
369
|
// Format error output
|
|
198
|
-
console.error(chalk.red(`\n❌
|
|
370
|
+
console.error(chalk.red(`\n❌ ${error.message}`));
|
|
199
371
|
|
|
200
372
|
if (suggestions && suggestions.length > 0) {
|
|
201
373
|
console.error(chalk.yellow('\n💡 Suggestions:'));
|
|
202
374
|
suggestions.forEach((suggestion) => {
|
|
203
|
-
console.error(chalk.yellow(`
|
|
375
|
+
console.error(chalk.yellow(` ${suggestion}`));
|
|
204
376
|
});
|
|
205
377
|
}
|
|
206
378
|
|
|
207
|
-
console.error(
|
|
208
|
-
chalk.gray(
|
|
209
|
-
'\n📖 For more help, visit: https://github.com/Paths-Design/coding-agent-working-standard'
|
|
210
|
-
)
|
|
211
|
-
);
|
|
379
|
+
console.error(chalk.blue(`\n📚 Documentation: ${docLink}`));
|
|
212
380
|
|
|
213
381
|
if (exit) {
|
|
214
382
|
process.exit(1);
|
|
@@ -250,4 +418,7 @@ module.exports = {
|
|
|
250
418
|
handleCliError,
|
|
251
419
|
validateEnvironment,
|
|
252
420
|
getRecoverySuggestions,
|
|
421
|
+
getDocumentationLink,
|
|
422
|
+
findSimilarCommand,
|
|
423
|
+
COMMAND_SUGGESTIONS,
|
|
253
424
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configure Jest for TypeScript project
|
|
3
|
+
* @param {string} projectDir - Project directory path
|
|
4
|
+
* @param {Object} options - Configuration options
|
|
5
|
+
* @returns {Promise<Object>} Configuration result
|
|
6
|
+
*/
|
|
7
|
+
export function configureJestForTypeScript(projectDir?: string, options?: any): Promise<any>;
|
|
8
|
+
/**
|
|
9
|
+
* Generate Jest configuration for TypeScript project
|
|
10
|
+
* @param {Object} options - Configuration options
|
|
11
|
+
* @returns {string} Jest configuration content
|
|
12
|
+
*/
|
|
13
|
+
export function generateJestConfig(options?: any): string;
|
|
14
|
+
/**
|
|
15
|
+
* Generate test setup file for TypeScript
|
|
16
|
+
* @returns {string} Setup file content
|
|
17
|
+
*/
|
|
18
|
+
export function generateTestSetup(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Install Jest and TypeScript dependencies
|
|
21
|
+
* @param {string} projectDir - Project directory
|
|
22
|
+
* @param {Object} packageJson - Existing package.json
|
|
23
|
+
* @returns {Promise<Object>} Installation result
|
|
24
|
+
*/
|
|
25
|
+
export function installJestDependencies(projectDir: string, packageJson: any): Promise<any>;
|
|
26
|
+
/**
|
|
27
|
+
* Get Jest configuration recommendations
|
|
28
|
+
* @param {string} projectDir - Project directory path
|
|
29
|
+
* @returns {Object} Recommendations
|
|
30
|
+
*/
|
|
31
|
+
export function getJestRecommendations(projectDir?: string): any;
|
|
32
|
+
//# sourceMappingURL=jest-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jest-config.d.ts","sourceRoot":"","sources":["../../src/generators/jest-config.js"],"names":[],"mappings":"AA+GA;;;;;GAKG;AACH,wDAJW,MAAM,kBAEJ,OAAO,KAAQ,CAgE3B;AAzKD;;;;GAIG;AACH,mDAFa,MAAM,CA0ClB;AAED;;;GAGG;AACH,qCAFa,MAAM,CAiBlB;AAED;;;;;GAKG;AACH,oDAJW,MAAM,qBAEJ,OAAO,KAAQ,CA2B3B;AAwED;;;;GAIG;AACH,oDAHW,MAAM,OAkDhB"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Jest Configuration Generator
|
|
3
|
+
* Generates Jest configuration for TypeScript projects
|
|
4
|
+
* @author @darianrosebrook
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generate Jest configuration for TypeScript project
|
|
13
|
+
* @param {Object} options - Configuration options
|
|
14
|
+
* @returns {string} Jest configuration content
|
|
15
|
+
*/
|
|
16
|
+
function generateJestConfig(options = {}) {
|
|
17
|
+
const {
|
|
18
|
+
preset = 'ts-jest',
|
|
19
|
+
testEnvironment = 'node',
|
|
20
|
+
rootDir = '.',
|
|
21
|
+
testMatch = ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
|
|
22
|
+
moduleFileExtensions = ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
|
23
|
+
collectCoverageFrom = ['src/**/*.ts', '!src/**/*.d.ts', '!src/**/*.test.ts'],
|
|
24
|
+
coverageThreshold = {
|
|
25
|
+
global: {
|
|
26
|
+
branches: 80,
|
|
27
|
+
functions: 80,
|
|
28
|
+
lines: 80,
|
|
29
|
+
statements: 80,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
} = options;
|
|
33
|
+
|
|
34
|
+
const config = {
|
|
35
|
+
preset,
|
|
36
|
+
testEnvironment,
|
|
37
|
+
rootDir,
|
|
38
|
+
testMatch,
|
|
39
|
+
moduleFileExtensions,
|
|
40
|
+
collectCoverageFrom,
|
|
41
|
+
coverageThreshold,
|
|
42
|
+
transform: {
|
|
43
|
+
'^.+\\.tsx?$': [
|
|
44
|
+
'ts-jest',
|
|
45
|
+
{
|
|
46
|
+
tsconfig: 'tsconfig.json',
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
moduleNameMapper: {
|
|
51
|
+
'^@/(.*)$': '<rootDir>/src/$1',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return `module.exports = ${JSON.stringify(config, null, 2)};\n`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generate test setup file for TypeScript
|
|
60
|
+
* @returns {string} Setup file content
|
|
61
|
+
*/
|
|
62
|
+
function generateTestSetup() {
|
|
63
|
+
return `/**
|
|
64
|
+
* Jest setup file for TypeScript tests
|
|
65
|
+
* @author @darianrosebrook
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
// Add custom matchers or global test setup here
|
|
69
|
+
beforeAll(() => {
|
|
70
|
+
// Global setup
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterAll(() => {
|
|
74
|
+
// Global teardown
|
|
75
|
+
});
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Install Jest and TypeScript dependencies
|
|
81
|
+
* @param {string} projectDir - Project directory
|
|
82
|
+
* @param {Object} packageJson - Existing package.json
|
|
83
|
+
* @returns {Promise<Object>} Installation result
|
|
84
|
+
*/
|
|
85
|
+
async function installJestDependencies(projectDir, packageJson) {
|
|
86
|
+
const dependencies = ['jest', '@types/jest', 'ts-jest'];
|
|
87
|
+
|
|
88
|
+
// Check which dependencies are already installed
|
|
89
|
+
const allDeps = {
|
|
90
|
+
...packageJson.dependencies,
|
|
91
|
+
...packageJson.devDependencies,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const toInstall = dependencies.filter((dep) => !(dep in allDeps));
|
|
95
|
+
|
|
96
|
+
if (toInstall.length === 0) {
|
|
97
|
+
return {
|
|
98
|
+
installed: false,
|
|
99
|
+
message: 'All Jest dependencies already installed',
|
|
100
|
+
dependencies: [],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
installed: false,
|
|
106
|
+
needsInstall: true,
|
|
107
|
+
dependencies: toInstall,
|
|
108
|
+
installCommand: `npm install --save-dev ${toInstall.join(' ')}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Configure Jest for TypeScript project
|
|
114
|
+
* @param {string} projectDir - Project directory path
|
|
115
|
+
* @param {Object} options - Configuration options
|
|
116
|
+
* @returns {Promise<Object>} Configuration result
|
|
117
|
+
*/
|
|
118
|
+
async function configureJestForTypeScript(projectDir = process.cwd(), options = {}) {
|
|
119
|
+
const { force = false, quiet = false } = options;
|
|
120
|
+
|
|
121
|
+
// Check if Jest config already exists
|
|
122
|
+
const jestConfigPath = path.join(projectDir, 'jest.config.js');
|
|
123
|
+
if (fs.existsSync(jestConfigPath) && !force) {
|
|
124
|
+
return {
|
|
125
|
+
configured: false,
|
|
126
|
+
skipped: true,
|
|
127
|
+
message: 'Jest configuration already exists',
|
|
128
|
+
path: jestConfigPath,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Generate Jest config
|
|
133
|
+
const jestConfig = generateJestConfig();
|
|
134
|
+
await fs.writeFile(jestConfigPath, jestConfig);
|
|
135
|
+
|
|
136
|
+
if (!quiet) {
|
|
137
|
+
console.log(chalk.green('✅ Created jest.config.js'));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Generate test setup file
|
|
141
|
+
const setupPath = path.join(projectDir, 'tests', 'setup.ts');
|
|
142
|
+
await fs.ensureDir(path.join(projectDir, 'tests'));
|
|
143
|
+
await fs.writeFile(setupPath, generateTestSetup());
|
|
144
|
+
|
|
145
|
+
if (!quiet) {
|
|
146
|
+
console.log(chalk.green('✅ Created tests/setup.ts'));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Update package.json with test script if needed
|
|
150
|
+
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
151
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
152
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
153
|
+
|
|
154
|
+
if (!packageJson.scripts) {
|
|
155
|
+
packageJson.scripts = {};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!packageJson.scripts.test) {
|
|
159
|
+
packageJson.scripts.test = 'jest';
|
|
160
|
+
packageJson.scripts['test:coverage'] = 'jest --coverage';
|
|
161
|
+
packageJson.scripts['test:watch'] = 'jest --watch';
|
|
162
|
+
|
|
163
|
+
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
164
|
+
|
|
165
|
+
if (!quiet) {
|
|
166
|
+
console.log(chalk.green('✅ Added test scripts to package.json'));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
configured: true,
|
|
173
|
+
files: [jestConfigPath, setupPath],
|
|
174
|
+
nextSteps: [
|
|
175
|
+
'Install dependencies: npm install --save-dev jest @types/jest ts-jest',
|
|
176
|
+
'Run tests: npm test',
|
|
177
|
+
'Run with coverage: npm run test:coverage',
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get Jest configuration recommendations
|
|
184
|
+
* @param {string} projectDir - Project directory path
|
|
185
|
+
* @returns {Object} Recommendations
|
|
186
|
+
*/
|
|
187
|
+
function getJestRecommendations(projectDir = process.cwd()) {
|
|
188
|
+
const recommendations = [];
|
|
189
|
+
const hasJestConfig = fs.existsSync(path.join(projectDir, 'jest.config.js'));
|
|
190
|
+
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
191
|
+
|
|
192
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
193
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
194
|
+
const allDeps = {
|
|
195
|
+
...packageJson.dependencies,
|
|
196
|
+
...packageJson.devDependencies,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
if (!hasJestConfig && !('jest' in allDeps)) {
|
|
200
|
+
recommendations.push({
|
|
201
|
+
type: 'missing_framework',
|
|
202
|
+
severity: 'high',
|
|
203
|
+
message: 'No testing framework detected',
|
|
204
|
+
fix: 'Install Jest: npm install --save-dev jest @types/jest ts-jest',
|
|
205
|
+
autoFixable: false,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if ('typescript' in allDeps && 'jest' in allDeps && !('ts-jest' in allDeps)) {
|
|
210
|
+
recommendations.push({
|
|
211
|
+
type: 'missing_ts_jest',
|
|
212
|
+
severity: 'high',
|
|
213
|
+
message: 'TypeScript project with Jest but missing ts-jest',
|
|
214
|
+
fix: 'Install ts-jest: npm install --save-dev ts-jest',
|
|
215
|
+
autoFixable: false,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!hasJestConfig && 'jest' in allDeps) {
|
|
220
|
+
recommendations.push({
|
|
221
|
+
type: 'missing_config',
|
|
222
|
+
severity: 'medium',
|
|
223
|
+
message: 'Jest installed but not configured',
|
|
224
|
+
fix: 'Run: caws scaffold to generate Jest configuration',
|
|
225
|
+
autoFixable: true,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
hasIssues: recommendations.length > 0,
|
|
232
|
+
recommendations,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = {
|
|
237
|
+
configureJestForTypeScript,
|
|
238
|
+
generateJestConfig,
|
|
239
|
+
generateTestSetup,
|
|
240
|
+
installJestDependencies,
|
|
241
|
+
getJestRecommendations,
|
|
242
|
+
};
|