@paths.design/caws-cli 8.0.1 → 8.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.
Files changed (44) hide show
  1. package/dist/commands/archive.d.ts +2 -1
  2. package/dist/commands/archive.d.ts.map +1 -1
  3. package/dist/commands/archive.js +114 -6
  4. package/dist/commands/burnup.d.ts.map +1 -1
  5. package/dist/commands/burnup.js +109 -10
  6. package/dist/commands/diagnose.js +1 -1
  7. package/dist/commands/mode.js +24 -14
  8. package/dist/commands/provenance.js +216 -93
  9. package/dist/commands/quality-gates.d.ts.map +1 -1
  10. package/dist/commands/quality-gates.js +3 -1
  11. package/dist/commands/specs.js +184 -6
  12. package/dist/commands/status.d.ts.map +1 -1
  13. package/dist/commands/status.js +134 -10
  14. package/dist/commands/templates.js +2 -2
  15. package/dist/error-handler.js +6 -98
  16. package/dist/generators/jest-config-generator.js +242 -0
  17. package/dist/index.js +4 -7
  18. package/dist/minimal-cli.js +3 -1
  19. package/dist/scaffold/claude-hooks.js +316 -0
  20. package/dist/scaffold/index.js +18 -0
  21. package/dist/templates/.claude/README.md +190 -0
  22. package/dist/templates/.claude/hooks/audit.sh +96 -0
  23. package/dist/templates/.claude/hooks/block-dangerous.sh +90 -0
  24. package/dist/templates/.claude/hooks/naming-check.sh +97 -0
  25. package/dist/templates/.claude/hooks/quality-check.sh +68 -0
  26. package/dist/templates/.claude/hooks/scan-secrets.sh +85 -0
  27. package/dist/templates/.claude/hooks/scope-guard.sh +105 -0
  28. package/dist/templates/.claude/hooks/validate-spec.sh +76 -0
  29. package/dist/templates/.claude/settings.json +95 -0
  30. package/dist/test-analysis.js +203 -10
  31. package/dist/utils/error-categories.js +210 -0
  32. package/dist/utils/quality-gates-utils.js +402 -0
  33. package/dist/utils/typescript-detector.js +36 -90
  34. package/dist/validation/spec-validation.js +59 -6
  35. package/package.json +5 -3
  36. package/templates/.claude/README.md +190 -0
  37. package/templates/.claude/hooks/audit.sh +96 -0
  38. package/templates/.claude/hooks/block-dangerous.sh +90 -0
  39. package/templates/.claude/hooks/naming-check.sh +97 -0
  40. package/templates/.claude/hooks/quality-check.sh +68 -0
  41. package/templates/.claude/hooks/scan-secrets.sh +85 -0
  42. package/templates/.claude/hooks/scope-guard.sh +105 -0
  43. package/templates/.claude/hooks/validate-spec.sh +76 -0
  44. package/templates/.claude/settings.json +95 -0
@@ -8,6 +8,7 @@ const fs = require('fs-extra');
8
8
  const path = require('path');
9
9
  const yaml = require('js-yaml');
10
10
  const chalk = require('chalk');
11
+ // child_process removed - execSync no longer used directly
11
12
  const { safeAsync, outputResult } = require('../error-handler');
12
13
  const { parallel } = require('../utils/async-utils');
13
14
 
@@ -181,14 +182,114 @@ async function loadWaiverStatus() {
181
182
  * @returns {Promise<Object>} Quality gates status
182
183
  */
183
184
  async function checkQualityGates() {
184
- // For now, return a placeholder
185
- // Quality gates are available via CLI or MCP
186
185
  return {
187
186
  checked: false,
188
187
  message: 'Run: caws quality-gates or use MCP tool caws_quality_gates_run for full gate status',
189
188
  };
190
189
  }
191
190
 
191
+ /**
192
+ * Get test coverage from coverage reports
193
+ * Looks for common coverage report locations and formats
194
+ * @returns {Promise<Object>} Coverage data with percentage and details
195
+ */
196
+ async function getTestCoverage() {
197
+ const coverageLocations = [
198
+ // Jest/Istanbul coverage
199
+ { path: 'coverage/coverage-summary.json', type: 'istanbul' },
200
+ { path: 'coverage/lcov-report/index.html', type: 'lcov-html' },
201
+ { path: 'coverage/coverage-final.json', type: 'istanbul-final' },
202
+ // NYC coverage
203
+ { path: '.nyc_output/coverage-summary.json', type: 'nyc' },
204
+ // C8 coverage
205
+ { path: 'coverage/c8/coverage-summary.json', type: 'c8' },
206
+ ];
207
+
208
+ try {
209
+ for (const loc of coverageLocations) {
210
+ const coveragePath = path.join(process.cwd(), loc.path);
211
+ if (await fs.pathExists(coveragePath)) {
212
+ if (loc.type === 'istanbul' || loc.type === 'nyc' || loc.type === 'c8') {
213
+ const content = await fs.readFile(coveragePath, 'utf8');
214
+ const data = JSON.parse(content);
215
+
216
+ // Istanbul/NYC format has a "total" key with coverage percentages
217
+ if (data.total) {
218
+ const lines = data.total.lines?.pct || 0;
219
+ const statements = data.total.statements?.pct || 0;
220
+ const branches = data.total.branches?.pct || 0;
221
+ const functions = data.total.functions?.pct || 0;
222
+
223
+ return {
224
+ available: true,
225
+ percentage: Math.round((lines + statements + branches + functions) / 4),
226
+ lines: Math.round(lines),
227
+ statements: Math.round(statements),
228
+ branches: Math.round(branches),
229
+ functions: Math.round(functions),
230
+ source: loc.path,
231
+ };
232
+ }
233
+ } else if (loc.type === 'istanbul-final') {
234
+ // coverage-final.json has per-file data, need to aggregate
235
+ const content = await fs.readFile(coveragePath, 'utf8');
236
+ const data = JSON.parse(content);
237
+
238
+ let totalStatements = 0;
239
+ let coveredStatements = 0;
240
+
241
+ for (const file of Object.values(data)) {
242
+ const s = file.s || {};
243
+ for (const count of Object.values(s)) {
244
+ totalStatements++;
245
+ if (count > 0) coveredStatements++;
246
+ }
247
+ }
248
+
249
+ if (totalStatements > 0) {
250
+ const percentage = Math.round((coveredStatements / totalStatements) * 100);
251
+ return {
252
+ available: true,
253
+ percentage,
254
+ source: loc.path,
255
+ };
256
+ }
257
+ }
258
+ }
259
+ }
260
+
261
+ // Try running test coverage command if no reports found
262
+ try {
263
+ // Check if package.json has a coverage script
264
+ const pkgPath = path.join(process.cwd(), 'package.json');
265
+ if (await fs.pathExists(pkgPath)) {
266
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
267
+ if (pkg.scripts && (pkg.scripts.coverage || pkg.scripts['test:coverage'])) {
268
+ return {
269
+ available: false,
270
+ percentage: null,
271
+ message: 'Run npm run coverage to generate coverage report',
272
+ };
273
+ }
274
+ }
275
+ } catch {
276
+ // Ignore package.json errors
277
+ }
278
+
279
+ return {
280
+ available: false,
281
+ percentage: null,
282
+ message: 'No coverage report found',
283
+ };
284
+ } catch (error) {
285
+ return {
286
+ available: false,
287
+ percentage: null,
288
+ message: `Error reading coverage: ${error.message}`,
289
+ };
290
+ }
291
+ }
292
+
192
293
  /**
193
294
  * Get time since last update
194
295
  * @param {string} timestamp - ISO timestamp
@@ -388,7 +489,7 @@ function getProgressColor(percentage) {
388
489
  * @param {Object} data - Status data
389
490
  * @param {string} currentMode - Current CAWS mode
390
491
  */
391
- function displayVisualStatus(data, currentMode) {
492
+ async function displayVisualStatus(data, currentMode) {
392
493
  const { spec, specs, hooks, provenance, waivers, gates } = data;
393
494
  const modes = require('../config/modes');
394
495
  const tierConfig = modes.getTier(currentMode);
@@ -497,12 +598,35 @@ function displayVisualStatus(data, currentMode) {
497
598
  );
498
599
  }
499
600
 
500
- // Test Coverage (placeholder for now)
501
- console.log(
502
- chalk.gray(
503
- ` Test Coverage: ${chalk.blue('Calculating...')} ${createProgressBar(0, 100)} 0%`
504
- )
505
- );
601
+ // Test Coverage
602
+ const coverage = await getTestCoverage();
603
+ if (coverage.available && coverage.percentage !== null) {
604
+ const coverageColor =
605
+ coverage.percentage >= 80
606
+ ? chalk.green
607
+ : coverage.percentage >= 50
608
+ ? chalk.yellow
609
+ : chalk.red;
610
+ const coverageBar = createProgressBar(coverage.percentage, 100);
611
+ console.log(
612
+ chalk.gray(
613
+ ` Test Coverage: ${coverageColor(`${coverage.percentage}%`)} ${coverageBar}`
614
+ )
615
+ );
616
+ if (coverage.lines !== undefined) {
617
+ console.log(
618
+ chalk.gray(
619
+ ` Lines: ${coverage.lines}% | Branches: ${coverage.branches}% | Functions: ${coverage.functions}%`
620
+ )
621
+ );
622
+ }
623
+ } else {
624
+ console.log(
625
+ chalk.gray(
626
+ ` Test Coverage: ${chalk.yellow('N/A')} ${createProgressBar(0, 100)} ${chalk.gray(coverage.message || 'No report')}`
627
+ )
628
+ );
629
+ }
506
630
 
507
631
  // Risk Tier Indicator
508
632
  const riskColor =
@@ -839,7 +963,7 @@ async function statusCommand(options = {}) {
839
963
  console.log(JSON.stringify(result, null, 2));
840
964
  } else {
841
965
  // Visual output
842
- displayVisualStatus(
966
+ await displayVisualStatus(
843
967
  {
844
968
  spec,
845
969
  specs,
@@ -149,7 +149,7 @@ function listTemplates() {
149
149
  const totalTemplates = Object.keys(templates).length;
150
150
 
151
151
  if (totalAvailable < totalTemplates) {
152
- console.log(chalk.yellow(`\n⏳ ${totalTemplates - totalAvailable} templates coming soon`));
152
+ console.log(chalk.yellow(`\n⏳ ${totalTemplates - totalAvailable} additional templates in development`));
153
153
  }
154
154
 
155
155
  console.log(chalk.blue('\n📚 Learn more:'));
@@ -181,7 +181,7 @@ function showTemplateInfo(templateId) {
181
181
  console.log(chalk.white(`Risk Tier: ${template.tier}`));
182
182
  console.log(
183
183
  chalk.white(
184
- `Status: ${template.available ? chalk.green('Available') : chalk.yellow('Coming Soon')}`
184
+ `Status: ${template.available ? chalk.green('Available') : chalk.yellow('In Development')}`
185
185
  )
186
186
  );
187
187
 
@@ -5,104 +5,12 @@
5
5
  */
6
6
 
7
7
  const chalk = require('chalk');
8
-
9
- /**
10
- * Error categories for better user experience
11
- */
12
- const ERROR_CATEGORIES = {
13
- VALIDATION: 'validation',
14
- PERMISSION: 'permission',
15
- FILESYSTEM: 'filesystem',
16
- NETWORK: 'network',
17
- CONFIGURATION: 'configuration',
18
- USER_INPUT: 'user_input',
19
- DEPENDENCY: 'dependency',
20
- UNKNOWN: 'unknown',
21
- };
22
-
23
- /**
24
- * Error code mappings for common system errors
25
- */
26
- const ERROR_CODES = {
27
- EACCES: ERROR_CATEGORIES.PERMISSION,
28
- EPERM: ERROR_CATEGORIES.PERMISSION,
29
- ENOENT: ERROR_CATEGORIES.FILESYSTEM,
30
- ENOTFOUND: ERROR_CATEGORIES.NETWORK,
31
- ECONNREFUSED: ERROR_CATEGORIES.NETWORK,
32
- ETIMEDOUT: ERROR_CATEGORIES.NETWORK,
33
- ENOSPC: ERROR_CATEGORIES.FILESYSTEM,
34
- EEXIST: ERROR_CATEGORIES.FILESYSTEM,
35
- EISDIR: ERROR_CATEGORIES.FILESYSTEM,
36
- ENOTDIR: ERROR_CATEGORIES.FILESYSTEM,
37
- };
38
-
39
- /**
40
- * Get error category from error object or message
41
- * @param {Error|string} error - Error object or message
42
- * @returns {string} Error category
43
- */
44
- function getErrorCategory(error) {
45
- const errorMessage = typeof error === 'string' ? error : error.message;
46
- const errorCode = typeof error === 'object' && error.code ? error.code : null;
47
-
48
- // Check error codes first
49
- if (errorCode && ERROR_CODES[errorCode]) {
50
- return ERROR_CODES[errorCode];
51
- }
52
-
53
- // Check message patterns
54
- const lowerMessage = errorMessage.toLowerCase();
55
-
56
- if (
57
- lowerMessage.includes('validation') ||
58
- lowerMessage.includes('invalid') ||
59
- lowerMessage.includes('required')
60
- ) {
61
- return ERROR_CATEGORIES.VALIDATION;
62
- }
63
-
64
- if (
65
- lowerMessage.includes('permission') ||
66
- lowerMessage.includes('access') ||
67
- lowerMessage.includes('denied')
68
- ) {
69
- return ERROR_CATEGORIES.PERMISSION;
70
- }
71
-
72
- if (
73
- lowerMessage.includes('file') ||
74
- lowerMessage.includes('directory') ||
75
- lowerMessage.includes('path')
76
- ) {
77
- return ERROR_CATEGORIES.FILESYSTEM;
78
- }
79
-
80
- if (
81
- lowerMessage.includes('network') ||
82
- lowerMessage.includes('connection') ||
83
- lowerMessage.includes('timeout')
84
- ) {
85
- return ERROR_CATEGORIES.NETWORK;
86
- }
87
-
88
- if (
89
- lowerMessage.includes('config') ||
90
- lowerMessage.includes('setting') ||
91
- lowerMessage.includes('option')
92
- ) {
93
- return ERROR_CATEGORIES.CONFIGURATION;
94
- }
95
-
96
- if (
97
- lowerMessage.includes('input') ||
98
- lowerMessage.includes('prompt') ||
99
- lowerMessage.includes('answer')
100
- ) {
101
- return ERROR_CATEGORIES.USER_INPUT;
102
- }
103
-
104
- return ERROR_CATEGORIES.UNKNOWN;
105
- }
8
+ const {
9
+ ERROR_CATEGORIES,
10
+ ERROR_CODES,
11
+ getErrorCategory,
12
+ getCategorySuggestions,
13
+ } = require('./utils/error-categories');
106
14
 
107
15
  /**
108
16
  * Enhanced error class with category and recovery suggestions
@@ -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
+ };
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  /**
4
4
  * @fileoverview CAWS CLI - Scaffolding tool for Coding Agent Workflow System
5
- * Provides commands to initialize new projects and scaffold existing ones with CAWS
5
+ * Provides commands to initialize new projects and scaffold existing ones with CAWS.
6
+ * Includes spec management, quality gates, and AI-assisted development workflows.
6
7
  * @author @darianrosebrook
7
8
  */
8
9
 
@@ -435,12 +436,8 @@ program
435
436
  .option('-v, --verbose', 'Show detailed error information', false)
436
437
  .action(qualityMonitorCommand);
437
438
 
438
- // Troubleshoot command - temporarily disabled due to registration issue
439
- // program
440
- // .command('troubleshoot [guide]')
441
- // .description('Display troubleshooting guides for common CAWS issues')
442
- // .option('-l, --list', 'List all available troubleshooting guides', false)
443
- // .action(troubleshootCommand);
439
+ // Troubleshoot command available via: caws diagnose --troubleshoot <guide>
440
+ // The standalone command was consolidated into the diagnose command.
444
441
 
445
442
  // Tool command
446
443
  program
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * @fileoverview Minimal CAWS CLI to test Chalk functionality
4
+ * @fileoverview Minimal CAWS CLI for testing and development
5
+ * Provides a lightweight CLI structure with version and help commands
6
+ * for quick verification of CLI functionality.
5
7
  * @author @darianrosebrook
6
8
  */
7
9