@paths.design/caws-cli 3.0.0 → 3.1.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 (95) hide show
  1. package/README.md +295 -150
  2. package/dist/budget-derivation.d.ts +35 -0
  3. package/dist/budget-derivation.d.ts.map +1 -0
  4. package/dist/budget-derivation.js +204 -0
  5. package/dist/cicd-optimizer.d.ts +142 -0
  6. package/dist/cicd-optimizer.d.ts.map +1 -0
  7. package/dist/cicd-optimizer.js +504 -0
  8. package/dist/commands/burnup.d.ts +6 -0
  9. package/dist/commands/burnup.d.ts.map +1 -0
  10. package/dist/commands/burnup.js +90 -0
  11. package/dist/commands/init.d.ts +5 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +514 -0
  14. package/dist/commands/provenance.d.ts +22 -0
  15. package/dist/commands/provenance.d.ts.map +1 -0
  16. package/dist/commands/provenance.js +594 -0
  17. package/dist/commands/tool.d.ts +13 -0
  18. package/dist/commands/tool.d.ts.map +1 -0
  19. package/dist/commands/tool.js +138 -0
  20. package/dist/commands/validate.d.ts +7 -0
  21. package/dist/commands/validate.d.ts.map +1 -0
  22. package/dist/commands/validate.js +80 -0
  23. package/dist/config/index.d.ts +29 -0
  24. package/dist/config/index.d.ts.map +1 -0
  25. package/dist/config/index.js +132 -0
  26. package/dist/error-handler.d.ts +50 -0
  27. package/dist/error-handler.d.ts.map +1 -0
  28. package/dist/error-handler.js +253 -0
  29. package/dist/generators/working-spec.d.ts +13 -0
  30. package/dist/generators/working-spec.d.ts.map +1 -0
  31. package/dist/generators/working-spec.js +204 -0
  32. package/dist/index-new.d.ts +5 -0
  33. package/dist/index-new.d.ts.map +1 -0
  34. package/dist/index-new.js +317 -0
  35. package/dist/index.d.ts +3 -12
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +100 -1659
  38. package/dist/index.js.backup +4711 -0
  39. package/dist/scaffold/cursor-hooks.d.ts +7 -0
  40. package/dist/scaffold/cursor-hooks.d.ts.map +1 -0
  41. package/dist/scaffold/cursor-hooks.js +152 -0
  42. package/dist/scaffold/index.d.ts +20 -0
  43. package/dist/scaffold/index.d.ts.map +1 -0
  44. package/dist/scaffold/index.js +486 -0
  45. package/dist/test-analysis.d.ts +182 -0
  46. package/dist/test-analysis.d.ts.map +1 -0
  47. package/dist/test-analysis.js +580 -0
  48. package/dist/tool-interface.d.ts +236 -0
  49. package/dist/tool-interface.d.ts.map +1 -0
  50. package/dist/tool-interface.js +314 -0
  51. package/dist/tool-loader.d.ts +77 -0
  52. package/dist/tool-loader.d.ts.map +1 -0
  53. package/dist/tool-loader.js +298 -0
  54. package/dist/tool-validator.d.ts +72 -0
  55. package/dist/tool-validator.d.ts.map +1 -0
  56. package/dist/tool-validator.js +387 -0
  57. package/dist/utils/detection.d.ts +7 -0
  58. package/dist/utils/detection.d.ts.map +1 -0
  59. package/dist/utils/detection.js +174 -0
  60. package/dist/utils/finalization.d.ts +17 -0
  61. package/dist/utils/finalization.d.ts.map +1 -0
  62. package/dist/utils/finalization.js +229 -0
  63. package/dist/utils/project-analysis.d.ts +14 -0
  64. package/dist/utils/project-analysis.d.ts.map +1 -0
  65. package/dist/utils/project-analysis.js +105 -0
  66. package/dist/validation/spec-validation.d.ts +29 -0
  67. package/dist/validation/spec-validation.d.ts.map +1 -0
  68. package/dist/validation/spec-validation.js +376 -0
  69. package/dist/waivers-manager.d.ts +167 -0
  70. package/dist/waivers-manager.d.ts.map +1 -0
  71. package/dist/waivers-manager.js +549 -0
  72. package/package.json +10 -12
  73. package/templates/.cursor/README.md +311 -0
  74. package/templates/.cursor/hooks/audit.sh +55 -0
  75. package/templates/.cursor/hooks/block-dangerous.sh +77 -0
  76. package/templates/.cursor/hooks/caws-quality-check.sh +52 -0
  77. package/templates/.cursor/hooks/caws-scope-guard.sh +74 -0
  78. package/templates/.cursor/hooks/caws-tool-validation.sh +121 -0
  79. package/templates/.cursor/hooks/format.sh +38 -0
  80. package/templates/.cursor/hooks/naming-check.sh +64 -0
  81. package/templates/.cursor/hooks/scan-secrets.sh +46 -0
  82. package/templates/.cursor/hooks/scope-guard.sh +52 -0
  83. package/templates/.cursor/hooks/validate-spec.sh +38 -0
  84. package/templates/.cursor/hooks.json +59 -0
  85. package/templates/.github/copilot/instructions.md +311 -0
  86. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +5 -0
  87. package/templates/.idea/runConfigurations/CAWS_Validate.xml +5 -0
  88. package/templates/.vscode/launch.json +56 -0
  89. package/templates/.vscode/settings.json +93 -0
  90. package/templates/.windsurf/workflows/caws-guided-development.md +92 -0
  91. package/templates/apps/tools/caws/README.md +1 -1
  92. package/templates/apps/tools/caws/prompt-lint.js.backup +274 -0
  93. package/templates/apps/tools/caws/provenance.js.backup +73 -0
  94. package/templates/apps/tools/caws/schemas/working-spec.schema.json +21 -3
  95. package/templates/codemod/test.js +93 -1
package/dist/index.js CHANGED
@@ -7,1685 +7,126 @@
7
7
  */
8
8
 
9
9
  const { Command } = require('commander');
10
+ // eslint-disable-next-line no-unused-vars
10
11
  const fs = require('fs-extra');
12
+ // eslint-disable-next-line no-unused-vars
11
13
  const path = require('path');
12
- const inquirer = require('inquirer').default || require('inquirer');
14
+ // eslint-disable-next-line no-unused-vars
13
15
  const yaml = require('js-yaml');
14
16
  const chalk = require('chalk');
15
17
 
16
- // Import language support (with fallback for when tools aren't available)
17
- let languageSupport = null;
18
- try {
19
- // Try multiple possible locations for language support
20
- const possiblePaths = [
21
- path.join(__dirname, '../../caws-template/apps/tools/caws/language-support.js'),
22
- path.join(__dirname, '../../../caws-template/apps/tools/caws/language-support.js'),
23
- path.join(process.cwd(), 'packages/caws-template/apps/tools/caws/language-support.js'),
24
- path.join(process.cwd(), 'caws-template/apps/tools/caws/language-support.js'),
25
- ];
26
-
27
- for (const testPath of possiblePaths) {
28
- try {
29
- languageSupport = require(testPath);
30
- // Only log if not running version command
31
- if (!process.argv.includes('--version') && !process.argv.includes('-V')) {
32
- console.log(`āœ… Loaded language support from: ${testPath}`);
33
- }
34
- break;
35
- } catch (pathError) {
36
- // Continue to next path
37
- }
38
- }
39
- } catch (error) {
40
- console.warn('āš ļø Language support tools not available');
41
- }
42
-
18
+ // Import configuration and utilities
19
+ const {
20
+ CLI_VERSION,
21
+ initializeGlobalSetup,
22
+ loadProvenanceTools,
23
+ initializeLanguageSupport,
24
+ } = require('./config');
25
+
26
+ // Import command handlers
27
+ const { initProject } = require('./commands/init');
28
+ const { validateCommand } = require('./commands/validate');
29
+ const { burnupCommand } = require('./commands/burnup');
30
+ const { testAnalysisCommand } = require('./test-analysis');
31
+ const { provenanceCommand } = require('./commands/provenance');
32
+ const { executeTool } = require('./commands/tool');
33
+
34
+ // Import scaffold functionality
35
+ const { scaffoldProject, setScaffoldDependencies } = require('./scaffold');
36
+
37
+ // Import validation functionality
38
+ // eslint-disable-next-line no-unused-vars
39
+ const { validateWorkingSpecWithSuggestions } = require('./validation/spec-validation');
40
+
41
+ // Import finalization utilities
42
+ const {
43
+ // eslint-disable-next-line no-unused-vars
44
+ finalizeProject,
45
+ // eslint-disable-next-line no-unused-vars
46
+ continueToSuccess,
47
+ setFinalizationDependencies,
48
+ } = require('./utils/finalization');
49
+
50
+ // Import generators
51
+ const { generateWorkingSpec, validateGeneratedSpec } = require('./generators/working-spec');
52
+
53
+ // Initialize global configuration
43
54
  const program = new Command();
44
55
 
45
- // CAWS Detection and Configuration
46
- function detectCAWSSetup(cwd = process.cwd()) {
47
- // Skip logging for version/help commands
48
- const isQuietCommand =
49
- process.argv.includes('--version') ||
50
- process.argv.includes('-V') ||
51
- process.argv.includes('--help');
52
-
53
- if (!isQuietCommand) {
54
- console.log(chalk.blue('šŸ” Detecting CAWS setup...'));
55
- }
56
-
57
- // Check for existing CAWS setup
58
- const cawsDir = path.join(cwd, '.caws');
59
- const hasCAWSDir = fs.existsSync(cawsDir);
60
-
61
- if (!hasCAWSDir) {
62
- if (!isQuietCommand) {
63
- console.log(chalk.gray('ā„¹ļø No .caws directory found - new project setup'));
64
- }
65
- return {
66
- type: 'new',
67
- hasCAWSDir: false,
68
- cawsDir: null,
69
- capabilities: [],
70
- hasTemplateDir: false,
71
- templateDir: null,
72
- };
73
- }
74
-
75
- // Analyze existing setup
76
- const files = fs.readdirSync(cawsDir);
77
- const hasWorkingSpec = fs.existsSync(path.join(cawsDir, 'working-spec.yaml'));
78
- const hasValidateScript = fs.existsSync(path.join(cawsDir, 'validate.js'));
79
- const hasPolicy = fs.existsSync(path.join(cawsDir, 'policy'));
80
- const hasSchemas = fs.existsSync(path.join(cawsDir, 'schemas'));
81
- const hasTemplates = fs.existsSync(path.join(cawsDir, 'templates'));
82
-
83
- // Check for multiple spec files (enhanced project pattern)
84
- const specFiles = files.filter((f) => f.endsWith('-spec.yaml'));
85
- const hasMultipleSpecs = specFiles.length > 1;
86
-
87
- // Check for tools directory (enhanced setup)
88
- const toolsDir = path.join(cwd, 'apps/tools/caws');
89
- const hasTools = fs.existsSync(toolsDir);
90
-
91
- // Determine setup type
92
- let setupType = 'basic';
93
- let capabilities = [];
94
-
95
- if (hasMultipleSpecs && hasWorkingSpec) {
96
- setupType = 'enhanced';
97
- capabilities.push('multiple-specs', 'working-spec', 'domain-specific');
98
- } else if (hasWorkingSpec) {
99
- setupType = 'standard';
100
- capabilities.push('working-spec');
101
- }
102
-
103
- if (hasValidateScript) {
104
- capabilities.push('validation');
105
- }
106
- if (hasPolicy) {
107
- capabilities.push('policies');
108
- }
109
- if (hasSchemas) {
110
- capabilities.push('schemas');
111
- }
112
- if (hasTemplates) {
113
- capabilities.push('templates');
114
- }
115
- if (hasTools) {
116
- capabilities.push('tools');
117
- }
118
-
119
- if (!isQuietCommand) {
120
- console.log(chalk.green(`āœ… Detected ${setupType} CAWS setup`));
121
- console.log(chalk.gray(` Capabilities: ${capabilities.join(', ')}`));
122
- }
123
-
124
- // Check for template directory - try multiple possible locations
125
- let templateDir = null;
126
- const possibleTemplatePaths = [
127
- // FIRST: Try bundled templates (for npm-installed CLI)
128
- path.resolve(__dirname, '../templates'),
129
- path.resolve(__dirname, 'templates'),
130
- // Try relative to current working directory (for monorepo setups)
131
- path.resolve(cwd, '../caws-template'),
132
- path.resolve(cwd, '../../caws-template'),
133
- path.resolve(cwd, '../../../caws-template'),
134
- path.resolve(cwd, 'packages/caws-template'),
135
- path.resolve(cwd, 'caws-template'),
136
- // Try relative to CLI location (for installed CLI)
137
- path.resolve(__dirname, '../caws-template'),
138
- path.resolve(__dirname, '../../caws-template'),
139
- path.resolve(__dirname, '../../../caws-template'),
140
- // Try absolute paths for CI environments
141
- path.resolve(process.cwd(), 'packages/caws-template'),
142
- path.resolve(process.cwd(), '../packages/caws-template'),
143
- path.resolve(process.cwd(), '../../packages/caws-template'),
144
- path.resolve(process.cwd(), '../../../packages/caws-template'),
145
- // Try from workspace root
146
- path.resolve(process.cwd(), 'caws-template'),
147
- // Try various other common locations
148
- '/home/runner/work/coding-agent-working-standard/coding-agent-working-standard/packages/caws-template',
149
- '/workspace/packages/caws-template',
150
- '/caws/packages/caws-template',
151
- ];
152
-
153
- for (const testPath of possibleTemplatePaths) {
154
- if (fs.existsSync(testPath)) {
155
- templateDir = testPath;
156
- if (!isQuietCommand) {
157
- console.log(`āœ… Found template directory: ${testPath}`);
158
- }
159
- break;
160
- }
161
- }
162
-
163
- const hasTemplateDir = templateDir !== null;
164
-
165
- return {
166
- type: setupType,
167
- hasCAWSDir: true,
168
- cawsDir,
169
- hasWorkingSpec,
170
- hasMultipleSpecs,
171
- hasValidateScript,
172
- hasPolicy,
173
- hasSchemas,
174
- hasTemplates,
175
- hasTools,
176
- hasTemplateDir,
177
- templateDir,
178
- capabilities,
179
- isEnhanced: setupType === 'enhanced',
180
- isAdvanced: hasTools || hasValidateScript,
181
- };
182
- }
183
-
184
- let cawsSetup = null;
185
-
186
- // Initialize global setup detection
187
- try {
188
- cawsSetup = detectCAWSSetup();
189
-
190
- // If no template dir found in current directory, check CLI installation location
191
- if (!cawsSetup.hasTemplateDir) {
192
- const cliTemplatePaths = [
193
- path.resolve(__dirname, '../templates'),
194
- path.resolve(__dirname, 'templates'),
195
- ];
196
-
197
- for (const testPath of cliTemplatePaths) {
198
- if (fs.existsSync(testPath)) {
199
- cawsSetup.templateDir = testPath;
200
- cawsSetup.hasTemplateDir = true;
201
- break;
202
- }
203
- }
204
- }
205
- } catch (error) {
206
- console.warn('āš ļø Failed to detect CAWS setup globally:', error.message);
207
- cawsSetup = {
208
- type: 'unknown',
209
- hasCAWSDir: false,
210
- cawsDir: null,
211
- hasWorkingSpec: false,
212
- hasMultipleSpecs: false,
213
- hasValidateScript: false,
214
- hasPolicy: false,
215
- hasSchemas: false,
216
- hasTemplates: false,
217
- hasTools: false,
218
- hasTemplateDir: false,
219
- templateDir: null,
220
- capabilities: [],
221
- isEnhanced: false,
222
- isAdvanced: false,
223
- };
224
- }
225
-
226
- // Dynamic imports based on setup
227
- let provenanceTools = null;
228
-
229
- // Function to load provenance tools dynamically
230
- function loadProvenanceTools() {
231
- if (provenanceTools) return provenanceTools; // Already loaded
232
-
233
- try {
234
- const setup = detectCAWSSetup();
235
- if (setup?.hasTemplateDir && setup?.templateDir) {
236
- const { generateProvenance, saveProvenance } = require(
237
- path.join(setup.templateDir, 'apps/tools/caws/provenance.js')
238
- );
239
- provenanceTools = { generateProvenance, saveProvenance };
240
- console.log('āœ… Loaded provenance tools from:', setup.templateDir);
241
- }
242
- } catch (error) {
243
- // Fallback for environments without template
244
- provenanceTools = null;
245
- console.warn('āš ļø Provenance tools not available:', error.message);
246
- }
247
-
248
- return provenanceTools;
249
- }
250
-
251
- const CLI_VERSION = require('../package.json').version;
252
-
253
- // Initialize JSON Schema validator - using simplified validation for CLI stability
254
- const validateWorkingSpec = (spec) => {
255
- try {
256
- // Basic structural validation for essential fields
257
- const requiredFields = [
258
- 'id',
259
- 'title',
260
- 'risk_tier',
261
- 'mode',
262
- 'change_budget',
263
- 'blast_radius',
264
- 'operational_rollback_slo',
265
- 'scope',
266
- 'invariants',
267
- 'acceptance',
268
- 'non_functional',
269
- 'contracts',
270
- ];
271
-
272
- for (const field of requiredFields) {
273
- if (!spec[field]) {
274
- return {
275
- valid: false,
276
- errors: [
277
- {
278
- instancePath: `/${field}`,
279
- message: `Missing required field: ${field}`,
280
- },
281
- ],
282
- };
283
- }
284
- }
285
-
286
- // Validate specific field formats
287
- if (!/^[A-Z]+-\d+$/.test(spec.id)) {
288
- return {
289
- valid: false,
290
- errors: [
291
- {
292
- instancePath: '/id',
293
- message: 'Project ID should be in format: PREFIX-NUMBER (e.g., FEAT-1234)',
294
- },
295
- ],
296
- };
297
- }
298
-
299
- // Validate experimental mode
300
- if (spec.experimental_mode) {
301
- if (typeof spec.experimental_mode !== 'object') {
302
- return {
303
- valid: false,
304
- errors: [
305
- {
306
- instancePath: '/experimental_mode',
307
- message:
308
- 'Experimental mode must be an object with enabled, rationale, and expires_at fields',
309
- },
310
- ],
311
- };
312
- }
313
-
314
- const requiredExpFields = ['enabled', 'rationale', 'expires_at'];
315
- for (const field of requiredExpFields) {
316
- if (!(field in spec.experimental_mode)) {
317
- return {
318
- valid: false,
319
- errors: [
320
- {
321
- instancePath: `/experimental_mode/${field}`,
322
- message: `Missing required experimental mode field: ${field}`,
323
- },
324
- ],
325
- };
326
- }
327
- }
328
-
329
- if (spec.experimental_mode.enabled && spec.risk_tier < 3) {
330
- return {
331
- valid: false,
332
- errors: [
333
- {
334
- instancePath: '/experimental_mode',
335
- message: 'Experimental mode can only be used with Tier 3 (low risk) changes',
336
- },
337
- ],
338
- };
339
- }
340
- }
341
-
342
- if (spec.risk_tier < 1 || spec.risk_tier > 3) {
343
- return {
344
- valid: false,
345
- errors: [
346
- {
347
- instancePath: '/risk_tier',
348
- message: 'Risk tier must be 1, 2, or 3',
349
- },
350
- ],
351
- };
352
- }
353
-
354
- if (!spec.scope || !spec.scope.in || spec.scope.in.length === 0) {
355
- return {
356
- valid: false,
357
- errors: [
358
- {
359
- instancePath: '/scope/in',
360
- message: 'Scope IN must not be empty',
361
- },
362
- ],
363
- };
364
- }
365
-
366
- return { valid: true };
367
- } catch (error) {
368
- return {
369
- valid: false,
370
- errors: [
371
- {
372
- instancePath: '',
373
- message: `Validation error: ${error.message}`,
374
- },
375
- ],
376
- };
377
- }
378
- };
379
-
380
- // Only log schema validation if not running quiet commands
381
- if (!process.argv.includes('--version') && !process.argv.includes('-V')) {
382
- console.log(chalk.green('āœ… Schema validation initialized successfully'));
383
- }
384
-
385
- /**
386
- * Generate working spec YAML with user input
387
- * @param {Object} answers - User responses
388
- * @returns {string} - Generated YAML content
389
- */
390
- function generateWorkingSpec(answers) {
391
- const template = {
392
- id: answers.projectId,
393
- title: answers.projectTitle,
394
- risk_tier: answers.riskTier,
395
- mode: answers.projectMode,
396
- change_budget: {
397
- max_files: answers.maxFiles,
398
- max_loc: answers.maxLoc,
399
- },
400
- blast_radius: {
401
- modules: answers.blastModules
402
- .split(',')
403
- .map((m) => m.trim())
404
- .filter((m) => m),
405
- data_migration: answers.dataMigration,
406
- },
407
- operational_rollback_slo: answers.rollbackSlo,
408
- threats: (answers.projectThreats || '')
409
- .split('\n')
410
- .map((t) => t.trim())
411
- .filter((t) => t && !t.startsWith('-') === false), // Allow lines starting with -
412
- scope: {
413
- in: (answers.scopeIn || '')
414
- .split(',')
415
- .map((s) => s.trim())
416
- .filter((s) => s),
417
- out: (answers.scopeOut || '')
418
- .split(',')
419
- .map((s) => s.trim())
420
- .filter((s) => s),
421
- },
422
- invariants: (answers.projectInvariants || '')
423
- .split('\n')
424
- .map((i) => i.trim())
425
- .filter((i) => i),
426
- acceptance: answers.acceptanceCriteria
427
- .split('\n')
428
- .filter((a) => a.trim())
429
- .map((criteria, index) => {
430
- const id = `A${index + 1}`;
431
- const upperCriteria = criteria.toUpperCase();
432
-
433
- // Try different variations of the format
434
- let given = '';
435
- let when = '';
436
- let then = '';
437
-
438
- if (
439
- upperCriteria.includes('GIVEN') &&
440
- upperCriteria.includes('WHEN') &&
441
- upperCriteria.includes('THEN')
442
- ) {
443
- given = criteria.split(/WHEN/i)[0]?.replace(/GIVEN/i, '').trim() || '';
444
- const whenThen = criteria.split(/WHEN/i)[1];
445
- when = whenThen?.split(/THEN/i)[0]?.trim() || '';
446
- then = whenThen?.split(/THEN/i)[1]?.trim() || '';
447
- } else {
448
- // Fallback: just split by lines and create simple criteria
449
- given = 'Current system state';
450
- when = criteria.replace(/^(GIVEN|WHEN|THEN)/i, '').trim();
451
- then = 'Expected behavior occurs';
452
- }
453
-
454
- return {
455
- id,
456
- given: given || 'Current system state',
457
- when: when || criteria,
458
- then: then || 'Expected behavior occurs',
459
- };
460
- }),
461
- non_functional: {
462
- a11y: answers.a11yRequirements
463
- .split(',')
464
- .map((a) => a.trim())
465
- .filter((a) => a),
466
- perf: { api_p95_ms: answers.perfBudget },
467
- security: answers.securityRequirements
468
- .split(',')
469
- .map((s) => s.trim())
470
- .filter((s) => s),
471
- },
472
- contracts: [
473
- {
474
- type: answers.contractType,
475
- path: answers.contractPath,
476
- },
477
- ],
478
- observability: {
479
- logs: answers.observabilityLogs
480
- .split(',')
481
- .map((l) => l.trim())
482
- .filter((l) => l),
483
- metrics: answers.observabilityMetrics
484
- .split(',')
485
- .map((m) => m.trim())
486
- .filter((m) => m),
487
- traces: answers.observabilityTraces
488
- .split(',')
489
- .map((t) => t.trim())
490
- .filter((t) => t),
491
- },
492
- migrations: (answers.migrationPlan || '')
493
- .split('\n')
494
- .map((m) => m.trim())
495
- .filter((m) => m),
496
- rollback: (answers.rollbackPlan || '')
497
- .split('\n')
498
- .map((r) => r.trim())
499
- .filter((r) => r),
500
- human_override: answers.needsOverride
501
- ? {
502
- enabled: true,
503
- approver: answers.overrideApprover,
504
- rationale: answers.overrideRationale,
505
- waived_gates: answers.waivedGates,
506
- approved_at: new Date().toISOString(),
507
- expires_at: new Date(
508
- Date.now() + answers.overrideExpiresDays * 24 * 60 * 60 * 1000
509
- ).toISOString(),
510
- }
511
- : undefined,
512
- experimental_mode: answers.isExperimental
513
- ? {
514
- enabled: true,
515
- rationale: answers.experimentalRationale,
516
- expires_at: new Date(
517
- Date.now() + answers.experimentalExpiresDays * 24 * 60 * 60 * 1000
518
- ).toISOString(),
519
- sandbox_location: answers.experimentalSandbox,
520
- }
521
- : undefined,
522
- ai_assessment: {
523
- confidence_level: answers.aiConfidence,
524
- uncertainty_areas: answers.uncertaintyAreas
525
- .split(',')
526
- .map((a) => a.trim())
527
- .filter((a) => a),
528
- complexity_factors: answers.complexityFactors
529
- .split(',')
530
- .map((f) => f.trim())
531
- .filter((f) => f),
532
- risk_factors: [], // Could be populated by AI analysis
533
- },
534
- };
535
-
536
- return yaml.dump(template, { indent: 2 });
537
- }
538
-
539
- /**
540
- * Validate generated working spec against JSON schema
541
- * @param {string} specContent - YAML spec content
542
- * @param {Object} answers - User responses for error context
543
- */
544
- function validateGeneratedSpec(specContent, _answers) {
545
- try {
546
- const spec = yaml.load(specContent);
547
-
548
- const isValid = validateWorkingSpec(spec);
549
-
550
- if (!isValid) {
551
- console.error(chalk.red('āŒ Generated working spec failed validation:'));
552
- validateWorkingSpec.errors.forEach((error) => {
553
- console.error(` - ${error.instancePath || 'root'}: ${error.message}`);
554
- });
555
-
556
- // Provide helpful guidance
557
- console.log(chalk.blue('\nšŸ’” Validation Tips:'));
558
- console.log(' - Ensure risk_tier is 1, 2, or 3');
559
- console.log(' - Check that scope.in is not empty');
560
- console.log(' - Verify invariants and acceptance criteria are provided');
561
- console.log(' - For tier 1 and 2, ensure contracts are specified');
562
-
563
- process.exit(1);
564
- }
565
-
566
- console.log(chalk.green('āœ… Generated working spec passed validation'));
567
- } catch (error) {
568
- console.error(chalk.red('āŒ Error validating working spec:'), error.message);
569
- process.exit(1);
570
- }
571
- }
572
-
573
- /**
574
- * Initialize a new project with CAWS
575
- */
576
- async function initProject(projectName, options) {
577
- console.log(chalk.cyan(`šŸš€ Initializing new CAWS project: ${projectName}`));
578
-
579
- let answers; // Will be set either interactively or with defaults
580
-
581
- try {
582
- // Validate project name
583
- if (!projectName || projectName.trim() === '') {
584
- console.error(chalk.red('āŒ Project name is required'));
585
- console.error(chalk.blue('šŸ’” Usage: caws init <project-name>'));
586
- process.exit(1);
587
- }
588
-
589
- // Special case: '.' means current directory, don't sanitize
590
- if (projectName !== '.') {
591
- // Sanitize project name
592
- const sanitizedName = projectName.replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase();
593
- if (sanitizedName !== projectName) {
594
- console.warn(chalk.yellow(`āš ļø Project name sanitized to: ${sanitizedName}`));
595
- projectName = sanitizedName;
596
- }
597
- }
598
-
599
- // Validate project name length
600
- if (projectName.length > 50) {
601
- console.error(chalk.red('āŒ Project name is too long (max 50 characters)'));
602
- console.error(chalk.blue('šŸ’” Usage: caws init <project-name>'));
603
- process.exit(1);
604
- }
605
-
606
- // Validate project name format
607
- if (projectName.length === 0) {
608
- console.error(chalk.red('āŒ Project name cannot be empty'));
609
- console.error(chalk.blue('šŸ’” Usage: caws init <project-name>'));
610
- process.exit(1);
611
- }
612
-
613
- // Check for invalid characters that should cause immediate failure
614
- if (projectName.includes('/') || projectName.includes('\\') || projectName.includes('..')) {
615
- console.error(chalk.red('āŒ Project name contains invalid characters'));
616
- console.error(chalk.blue('šŸ’” Usage: caws init <project-name>'));
617
- console.error(chalk.blue('šŸ’” Project name should not contain: / \\ ..'));
618
- process.exit(1);
619
- }
620
-
621
- // Determine if initializing in current directory
622
- const initInCurrentDir = projectName === '.';
623
- const targetDir = initInCurrentDir ? process.cwd() : path.resolve(process.cwd(), projectName);
624
-
625
- // Check if directory already exists (skip check for current directory)
626
- if (!initInCurrentDir && fs.existsSync(projectName)) {
627
- console.error(chalk.red(`āŒ Directory ${projectName} already exists`));
628
- console.error(chalk.blue('šŸ’” Choose a different name or remove the existing directory'));
629
- process.exit(1);
630
- }
631
-
632
- // Save the original template directory before changing directories
633
- const originalTemplateDir = cawsSetup?.hasTemplateDir ? cawsSetup.templateDir : null;
634
-
635
- // Check for existing agents.md/caws.md in target directory
636
- const existingAgentsMd = fs.existsSync(path.join(targetDir, 'agents.md'));
637
- const existingCawsMd = fs.existsSync(path.join(targetDir, 'caws.md'));
638
-
639
- // Create project directory and change to it (unless already in current directory)
640
- if (!initInCurrentDir) {
641
- await fs.ensureDir(projectName);
642
- process.chdir(projectName);
643
- console.log(chalk.green(`šŸ“ Created project directory: ${projectName}`));
644
- } else {
645
- console.log(chalk.green(`šŸ“ Initializing in current directory`));
646
- }
647
-
648
- // Detect and adapt to existing setup
649
- const currentSetup = detectCAWSSetup(process.cwd());
650
-
651
- if (currentSetup.type === 'new') {
652
- // Create minimal CAWS structure
653
- await fs.ensureDir('.caws');
654
- await fs.ensureDir('.agent');
655
- console.log(chalk.blue('ā„¹ļø Created basic CAWS structure'));
56
+ // Initialize global state
57
+ const cawsSetup = initializeGlobalSetup();
58
+ const languageSupport = initializeLanguageSupport();
656
59
 
657
- // Copy agents.md guide if templates are available
658
- if (originalTemplateDir) {
659
- try {
660
- const agentsMdSource = path.join(originalTemplateDir, 'agents.md');
661
- let targetFile = 'agents.md';
662
-
663
- if (fs.existsSync(agentsMdSource)) {
664
- // Use the pre-checked values for conflicts
665
- if (existingAgentsMd) {
666
- // Conflict: user already has agents.md
667
- if (options.interactive && !options.nonInteractive) {
668
- // Interactive mode: ask user
669
- const overwriteAnswer = await inquirer.prompt([
670
- {
671
- type: 'confirm',
672
- name: 'overwrite',
673
- message: 'āš ļø agents.md already exists. Overwrite with CAWS guide?',
674
- default: false,
675
- },
676
- ]);
677
-
678
- if (overwriteAnswer.overwrite) {
679
- targetFile = 'agents.md';
680
- } else {
681
- targetFile = 'caws.md';
682
- }
683
- } else {
684
- // Non-interactive mode: use caws.md instead
685
- targetFile = 'caws.md';
686
- console.log(chalk.blue('ā„¹ļø agents.md exists, using caws.md for CAWS guide'));
687
- }
688
- }
689
-
690
- // If caws.md also exists and that's our target, skip
691
- if (targetFile === 'caws.md' && existingCawsMd) {
692
- console.log(
693
- chalk.yellow('āš ļø Both agents.md and caws.md exist, skipping guide copy')
694
- );
695
- } else {
696
- const agentsMdDest = path.join(process.cwd(), targetFile);
697
- await fs.copyFile(agentsMdSource, agentsMdDest);
698
- console.log(chalk.green(`āœ… Added ${targetFile} guide`));
699
- }
700
- }
701
- } catch (templateError) {
702
- console.warn(chalk.yellow('āš ļø Could not copy agents guide:'), templateError.message);
703
- }
704
- }
705
- } else {
706
- // Already has CAWS setup
707
- console.log(chalk.green('āœ… CAWS project detected - skipping template copy'));
708
- }
709
-
710
- // Set default answers for non-interactive mode
711
- if (!options.interactive || options.nonInteractive) {
712
- // Use directory name for current directory init
713
- const displayName = initInCurrentDir ? path.basename(process.cwd()) : projectName;
714
-
715
- answers = {
716
- projectId: displayName.toUpperCase().replace(/[^A-Z0-9]/g, '-') + '-001',
717
- projectTitle: displayName.charAt(0).toUpperCase() + displayName.slice(1).replace(/-/g, ' '),
718
- riskTier: 2,
719
- projectMode: 'feature',
720
- maxFiles: 25,
721
- maxLoc: 1000,
722
- blastModules: 'core,ui',
723
- dataMigration: false,
724
- rollbackSlo: '5m',
725
- projectThreats: 'Standard project threats',
726
- scopeIn: 'project files',
727
- scopeOut: 'external dependencies',
728
- projectInvariants: 'System maintains consistency',
729
- acceptanceCriteria: 'GIVEN current state WHEN action THEN expected result',
730
- a11yRequirements: 'keyboard navigation, screen reader support',
731
- perfBudget: 250,
732
- securityRequirements: 'input validation, authentication',
733
- contractType: 'openapi',
734
- contractPath: 'apps/contracts/api.yaml',
735
- observabilityLogs: 'auth.success,api.request',
736
- observabilityMetrics: 'requests_total',
737
- observabilityTraces: 'api_flow',
738
- migrationPlan: 'Standard deployment process',
739
- rollbackPlan: 'Feature flag disable and rollback',
740
- needsOverride: false,
741
- overrideRationale: '',
742
- overrideApprover: '',
743
- waivedGates: [],
744
- overrideExpiresDays: 7,
745
- isExperimental: false,
746
- experimentalRationale: '',
747
- experimentalSandbox: 'experimental/',
748
- experimentalExpiresDays: 14,
749
- aiConfidence: 7,
750
- uncertaintyAreas: '',
751
- complexityFactors: '',
752
- };
753
-
754
- // Generate working spec for non-interactive mode
755
- const workingSpecContent = generateWorkingSpec(answers);
756
-
757
- // Validate the generated spec
758
- validateGeneratedSpec(workingSpecContent, answers);
759
-
760
- // Save the working spec
761
- await fs.writeFile('.caws/working-spec.yaml', workingSpecContent);
762
-
763
- console.log(chalk.green('āœ… Working spec generated and validated'));
764
-
765
- // Finalize project with provenance and git initialization
766
- await finalizeProject(projectName, options, answers);
767
-
768
- continueToSuccess();
769
- return;
770
- }
771
-
772
- if (options.interactive && !options.nonInteractive) {
773
- // Interactive setup with enhanced prompts
774
- console.log(chalk.cyan('šŸ”§ Starting interactive project configuration...'));
775
- console.log(chalk.blue('šŸ’” Press Ctrl+C at any time to exit and use defaults'));
776
-
777
- const questions = [
778
- {
779
- type: 'input',
780
- name: 'projectId',
781
- message: 'šŸ“‹ Project ID (e.g., FEAT-1234, AUTH-456):',
782
- default: projectName.toUpperCase().replace(/[^A-Z0-9]/g, '-') + '-001',
783
- validate: (input) => {
784
- if (!input.trim()) return 'Project ID is required';
785
- const pattern = /^[A-Z]+-\d+$/;
786
- if (!pattern.test(input)) {
787
- return 'Project ID should be in format: PREFIX-NUMBER (e.g., FEAT-1234)';
788
- }
789
- return true;
790
- },
791
- },
792
- {
793
- type: 'input',
794
- name: 'projectTitle',
795
- message: 'šŸ“ Project Title (descriptive name):',
796
- default: projectName.charAt(0).toUpperCase() + projectName.slice(1).replace(/-/g, ' '),
797
- validate: (input) => {
798
- if (!input.trim()) return 'Project title is required';
799
- if (input.trim().length < 8) {
800
- return 'Project title should be at least 8 characters long';
801
- }
802
- return true;
803
- },
804
- },
805
- {
806
- type: 'list',
807
- name: 'riskTier',
808
- message: 'āš ļø Risk Tier (higher tier = more rigor):',
809
- choices: [
810
- {
811
- name: 'šŸ”“ Tier 1 - Critical (auth, billing, migrations) - Max rigor',
812
- value: 1,
813
- },
814
- {
815
- name: '🟔 Tier 2 - Standard (features, APIs) - Standard rigor',
816
- value: 2,
817
- },
818
- {
819
- name: '🟢 Tier 3 - Low Risk (UI, tooling) - Basic rigor',
820
- value: 3,
821
- },
822
- ],
823
- default: 2,
824
- },
825
- {
826
- type: 'list',
827
- name: 'projectMode',
828
- message: 'šŸŽÆ Project Mode:',
829
- choices: [
830
- { name: '✨ feature (new functionality)', value: 'feature' },
831
- { name: 'šŸ”„ refactor (code restructuring)', value: 'refactor' },
832
- { name: 'šŸ› fix (bug fixes)', value: 'fix' },
833
- { name: 'šŸ“š doc (documentation)', value: 'doc' },
834
- { name: '🧹 chore (maintenance)', value: 'chore' },
835
- ],
836
- default: 'feature',
837
- },
838
- {
839
- type: 'number',
840
- name: 'maxFiles',
841
- message: 'šŸ“Š Max files to change:',
842
- default: (answers) => {
843
- // Dynamic defaults based on risk tier
844
- switch (answers.riskTier) {
845
- case 1:
846
- return 40;
847
- case 2:
848
- return 25;
849
- case 3:
850
- return 15;
851
- default:
852
- return 25;
853
- }
854
- },
855
- validate: (input) => {
856
- if (input < 1) return 'Must change at least 1 file';
857
- return true;
858
- },
859
- },
860
- {
861
- type: 'number',
862
- name: 'maxLoc',
863
- message: 'šŸ“ Max lines of code to change:',
864
- default: (answers) => {
865
- // Dynamic defaults based on risk tier
866
- switch (answers.riskTier) {
867
- case 1:
868
- return 1500;
869
- case 2:
870
- return 1000;
871
- case 3:
872
- return 600;
873
- default:
874
- return 1000;
875
- }
876
- },
877
- validate: (input) => {
878
- if (input < 1) return 'Must change at least 1 line';
879
- return true;
880
- },
881
- },
882
- {
883
- type: 'input',
884
- name: 'blastModules',
885
- message: 'šŸ’„ Blast Radius - Affected modules (comma-separated):',
886
- default: 'core,api',
887
- validate: (input) => {
888
- if (!input.trim()) return 'At least one module must be specified';
889
- return true;
890
- },
891
- },
892
- {
893
- type: 'confirm',
894
- name: 'dataMigration',
895
- message: 'šŸ—„ļø Requires data migration?',
896
- default: false,
897
- },
898
- {
899
- type: 'input',
900
- name: 'rollbackSlo',
901
- message: 'ā±ļø Operational rollback SLO (e.g., 5m, 1h, 24h):',
902
- default: '5m',
903
- validate: (input) => {
904
- const pattern = /^([0-9]+m|[0-9]+h|[0-9]+d)$/;
905
- if (!pattern.test(input)) {
906
- return 'SLO should be in format: NUMBER + m/h/d (e.g., 5m, 1h, 24h)';
907
- }
908
- return true;
909
- },
910
- },
911
- {
912
- type: 'editor',
913
- name: 'projectThreats',
914
- message: 'āš ļø Project Threats & Risks (one per line, ESC to finish):',
915
- default: (answers) => {
916
- const baseThreats =
917
- '- Race condition in concurrent operations\n- Performance degradation under load';
918
- if (answers.dataMigration) {
919
- return baseThreats + '\n- Data migration failure\n- Inconsistent data state';
920
- }
921
- return baseThreats;
922
- },
923
- },
924
- {
925
- type: 'input',
926
- name: 'scopeIn',
927
- message: "šŸŽÆ Scope IN - What's included (comma-separated):",
928
- default: (answers) => {
929
- if (answers.projectMode === 'feature') return 'user authentication, api endpoints';
930
- if (answers.projectMode === 'refactor') return 'authentication module, user service';
931
- if (answers.projectMode === 'fix') return 'error handling, validation';
932
- return 'project files';
933
- },
934
- validate: (input) => {
935
- if (!input.trim()) return 'At least one scope item must be specified';
936
- return true;
937
- },
938
- },
939
- {
940
- type: 'input',
941
- name: 'scopeOut',
942
- message: "🚫 Scope OUT - What's excluded (comma-separated):",
943
- default: (answers) => {
944
- if (answers.projectMode === 'feature')
945
- return 'legacy authentication, deprecated endpoints';
946
- if (answers.projectMode === 'refactor')
947
- return 'external dependencies, configuration files';
948
- return 'unrelated features';
949
- },
950
- },
951
- {
952
- type: 'editor',
953
- name: 'projectInvariants',
954
- message: 'šŸ›”ļø System Invariants (one per line, ESC to finish):',
955
- default:
956
- '- System remains available\n- Data consistency maintained\n- User sessions preserved',
957
- },
958
- {
959
- type: 'editor',
960
- name: 'acceptanceCriteria',
961
- message: 'āœ… Acceptance Criteria (GIVEN...WHEN...THEN, one per line, ESC to finish):',
962
- default: (answers) => {
963
- if (answers.projectMode === 'feature') {
964
- return 'GIVEN user is authenticated WHEN accessing protected endpoint THEN access is granted\nGIVEN invalid credentials WHEN attempting login THEN access is denied';
965
- }
966
- if (answers.projectMode === 'fix') {
967
- return 'GIVEN existing functionality WHEN applying fix THEN behavior is preserved\nGIVEN error condition WHEN fix is applied THEN error is resolved';
968
- }
969
- return 'GIVEN current system state WHEN change is applied THEN expected behavior occurs';
970
- },
971
- validate: (input) => {
972
- if (!input.trim()) return 'At least one acceptance criterion is required';
973
- const lines = input
974
- .trim()
975
- .split('\n')
976
- .filter((line) => line.trim());
977
- if (lines.length === 0) return 'At least one acceptance criterion is required';
978
- return true;
979
- },
980
- },
981
- {
982
- type: 'input',
983
- name: 'a11yRequirements',
984
- message: '♿ Accessibility Requirements (comma-separated):',
985
- default: 'keyboard navigation, screen reader support, color contrast',
986
- },
987
- {
988
- type: 'number',
989
- name: 'perfBudget',
990
- message: '⚔ Performance Budget (API p95 latency in ms):',
991
- default: 250,
992
- validate: (input) => {
993
- if (input < 1) return 'Performance budget must be at least 1ms';
994
- if (input > 10000) return 'Performance budget seems too high (max 10s)';
995
- return true;
996
- },
997
- },
998
- {
999
- type: 'input',
1000
- name: 'securityRequirements',
1001
- message: 'šŸ”’ Security Requirements (comma-separated):',
1002
- default: 'input validation, rate limiting, authentication, authorization',
1003
- },
1004
- {
1005
- type: 'list',
1006
- name: 'contractType',
1007
- message: 'šŸ“„ Contract Type:',
1008
- choices: [
1009
- { name: 'OpenAPI (REST APIs)', value: 'openapi' },
1010
- { name: 'GraphQL Schema', value: 'graphql' },
1011
- { name: 'Protocol Buffers', value: 'proto' },
1012
- { name: 'Pact (consumer-driven)', value: 'pact' },
1013
- ],
1014
- default: 'openapi',
1015
- },
1016
- {
1017
- type: 'input',
1018
- name: 'contractPath',
1019
- message: 'šŸ“ Contract File Path:',
1020
- default: (answers) => {
1021
- if (answers.contractType === 'openapi') return 'apps/contracts/api.yaml';
1022
- if (answers.contractType === 'graphql') return 'apps/contracts/schema.graphql';
1023
- if (answers.contractType === 'proto') return 'apps/contracts/service.proto';
1024
- if (answers.contractType === 'pact') return 'apps/contracts/pacts/';
1025
- return 'apps/contracts/api.yaml';
1026
- },
1027
- },
1028
- {
1029
- type: 'input',
1030
- name: 'observabilityLogs',
1031
- message: 'šŸ“ Observability - Log Events (comma-separated):',
1032
- default: 'auth.success, auth.failure, api.request, api.response',
1033
- },
1034
- {
1035
- type: 'input',
1036
- name: 'observabilityMetrics',
1037
- message: 'šŸ“Š Observability - Metrics (comma-separated):',
1038
- default: 'auth_attempts_total, auth_success_total, api_requests_total, api_errors_total',
1039
- },
1040
- {
1041
- type: 'input',
1042
- name: 'observabilityTraces',
1043
- message: 'šŸ” Observability - Traces (comma-separated):',
1044
- default: 'auth_flow, api_request',
1045
- },
1046
- {
1047
- type: 'editor',
1048
- name: 'migrationPlan',
1049
- message: 'šŸ”„ Migration Plan (one per line, ESC to finish):',
1050
- default: (answers) => {
1051
- if (answers.dataMigration) {
1052
- return '- Create new database schema\n- Add new auth table\n- Migrate existing users\n- Validate data integrity';
1053
- }
1054
- return '- Deploy feature flags\n- Roll out gradually\n- Monitor metrics';
1055
- },
1056
- validate: (input) => {
1057
- if (!input.trim()) return 'Migration plan is required';
1058
- return true;
1059
- },
1060
- },
1061
- {
1062
- type: 'editor',
1063
- name: 'rollbackPlan',
1064
- message: 'šŸ”™ Rollback Plan (one per line, ESC to finish):',
1065
- default: (answers) => {
1066
- if (answers.dataMigration) {
1067
- return '- Feature flag kill-switch\n- Database rollback script\n- Restore from backup\n- Verify system state';
1068
- }
1069
- return '- Feature flag disable\n- Deploy previous version\n- Monitor for issues';
1070
- },
1071
- validate: (input) => {
1072
- if (!input.trim()) return 'Rollback plan is required';
1073
- return true;
1074
- },
1075
- },
1076
- {
1077
- type: 'confirm',
1078
- name: 'needsOverride',
1079
- message: '🚨 Need human override for urgent/low-risk change?',
1080
- default: false,
1081
- },
1082
- {
1083
- type: 'input',
1084
- name: 'overrideRationale',
1085
- message: 'šŸ“ Override rationale (urgency, low risk, etc.):',
1086
- when: (answers) => answers.needsOverride,
1087
- validate: (input) => {
1088
- if (!input.trim()) return 'Rationale is required for override';
1089
- return true;
1090
- },
1091
- },
1092
- {
1093
- type: 'input',
1094
- name: 'overrideApprover',
1095
- message: 'šŸ‘¤ Override approver (GitHub username or email):',
1096
- when: (answers) => answers.needsOverride,
1097
- validate: (input) => {
1098
- if (!input.trim()) return 'Approver is required for override';
1099
- return true;
1100
- },
1101
- },
1102
- {
1103
- type: 'checkbox',
1104
- name: 'waivedGates',
1105
- message: 'āš ļø Gates to waive (select with space):',
1106
- choices: [
1107
- { name: 'Coverage testing', value: 'coverage' },
1108
- { name: 'Mutation testing', value: 'mutation' },
1109
- { name: 'Contract testing', value: 'contracts' },
1110
- { name: 'Manual review', value: 'manual_review' },
1111
- { name: 'Trust score check', value: 'trust_score' },
1112
- ],
1113
- when: (answers) => answers.needsOverride,
1114
- validate: (input) => {
1115
- if (input.length === 0) return 'At least one gate must be waived';
1116
- return true;
1117
- },
1118
- },
1119
- {
1120
- type: 'number',
1121
- name: 'overrideExpiresDays',
1122
- message: 'ā° Override expires in how many days?',
1123
- default: 7,
1124
- when: (answers) => answers.needsOverride,
1125
- validate: (input) => {
1126
- if (input < 1) return 'Must expire in at least 1 day';
1127
- if (input > 30) return 'Cannot exceed 30 days';
1128
- return true;
1129
- },
1130
- },
1131
- {
1132
- type: 'confirm',
1133
- name: 'isExperimental',
1134
- message: '🧪 Experimental/Prototype mode? (Reduced requirements for sandbox code)',
1135
- default: false,
1136
- },
1137
- {
1138
- type: 'input',
1139
- name: 'experimentalRationale',
1140
- message: 'šŸ”¬ Experimental rationale (what are you exploring?):',
1141
- when: (answers) => answers.isExperimental,
1142
- validate: (input) => {
1143
- if (!input.trim()) return 'Rationale is required for experimental mode';
1144
- return true;
1145
- },
1146
- },
1147
- {
1148
- type: 'input',
1149
- name: 'experimentalSandbox',
1150
- message: 'šŸ“ Sandbox location (directory or feature flag):',
1151
- default: 'experimental/',
1152
- when: (answers) => answers.isExperimental,
1153
- validate: (input) => {
1154
- if (!input.trim()) return 'Sandbox location is required';
1155
- return true;
1156
- },
1157
- },
1158
- {
1159
- type: 'number',
1160
- name: 'experimentalExpiresDays',
1161
- message: 'ā° Experimental code expires in how many days?',
1162
- default: 14,
1163
- when: (answers) => answers.isExperimental,
1164
- validate: (input) => {
1165
- if (input < 1) return 'Must expire in at least 1 day';
1166
- if (input > 90) return 'Cannot exceed 90 days for experimental code';
1167
- return true;
1168
- },
1169
- },
1170
- {
1171
- type: 'number',
1172
- name: 'aiConfidence',
1173
- message: 'šŸ¤– AI confidence level (1-10, 10 = very confident):',
1174
- default: 7,
1175
- validate: (input) => {
1176
- if (input < 1 || input > 10) return 'Must be between 1 and 10';
1177
- return true;
1178
- },
1179
- },
1180
- {
1181
- type: 'input',
1182
- name: 'uncertaintyAreas',
1183
- message: 'ā“ Areas of uncertainty (comma-separated):',
1184
- default: '',
1185
- },
1186
- {
1187
- type: 'input',
1188
- name: 'complexityFactors',
1189
- message: 'šŸ”§ Complexity factors (comma-separated):',
1190
- default: '',
1191
- },
1192
- ];
1193
-
1194
- console.log(chalk.cyan('ā³ Gathering project requirements...'));
1195
-
1196
- let answers;
1197
- try {
1198
- answers = await inquirer.prompt(questions);
1199
- } catch (error) {
1200
- if (error.isTtyError) {
1201
- console.error(chalk.red('āŒ Interactive prompts not supported in this environment'));
1202
- console.error(chalk.blue('šŸ’” Run with --non-interactive flag to use defaults'));
1203
- process.exit(1);
1204
- } else {
1205
- console.error(chalk.red('āŒ Error during interactive setup:'), error.message);
1206
- process.exit(1);
1207
- }
1208
- }
1209
-
1210
- console.log(chalk.green('āœ… Project requirements gathered successfully!'));
1211
-
1212
- // Show summary before generating spec
1213
- console.log(chalk.bold('\nšŸ“‹ Configuration Summary:'));
1214
- console.log(` ${chalk.cyan('Project')}: ${answers.projectTitle} (${answers.projectId})`);
1215
- console.log(
1216
- ` ${chalk.cyan('Mode')}: ${answers.projectMode} | ${chalk.cyan('Tier')}: ${answers.riskTier}`
1217
- );
1218
- console.log(` ${chalk.cyan('Budget')}: ${answers.maxFiles} files, ${answers.maxLoc} lines`);
1219
- console.log(` ${chalk.cyan('Data Migration')}: ${answers.dataMigration ? 'Yes' : 'No'}`);
1220
- console.log(` ${chalk.cyan('Rollback SLO')}: ${answers.rollbackSlo}`);
1221
-
1222
- // Generate working spec
1223
- const workingSpecContent = generateWorkingSpec(answers);
1224
-
1225
- // Validate the generated spec
1226
- validateGeneratedSpec(workingSpecContent, answers);
1227
-
1228
- // Save the working spec
1229
- await fs.writeFile('.caws/working-spec.yaml', workingSpecContent);
1230
-
1231
- console.log(chalk.green('āœ… Working spec generated and validated'));
1232
-
1233
- // Finalize project with provenance and git initialization
1234
- await finalizeProject(projectName, options, answers);
1235
-
1236
- continueToSuccess();
1237
- }
1238
- } catch (error) {
1239
- console.error(chalk.red('āŒ Error during project initialization:'), error.message);
1240
-
1241
- // Cleanup on error
1242
- if (fs.existsSync(projectName)) {
1243
- console.log(chalk.cyan('🧹 Cleaning up failed initialization...'));
1244
- try {
1245
- await fs.remove(projectName);
1246
- console.log(chalk.green('āœ… Cleanup completed'));
1247
- } catch (cleanupError) {
1248
- console.warn(
1249
- chalk.yellow('āš ļø Could not clean up:'),
1250
- cleanupError?.message || cleanupError
1251
- );
1252
- }
1253
- }
1254
-
1255
- process.exit(1);
1256
- }
1257
- }
1258
-
1259
- // Generate provenance manifest and git initialization (for both modes)
1260
- async function finalizeProject(projectName, options, answers) {
1261
- try {
1262
- // Detect and configure language support
1263
- if (languageSupport) {
1264
- console.log(chalk.cyan('šŸ” Detecting project language...'));
1265
- const detectedLanguage = languageSupport.detectProjectLanguage();
1266
-
1267
- if (detectedLanguage !== 'unknown') {
1268
- console.log(chalk.green(`āœ… Detected language: ${detectedLanguage}`));
1269
-
1270
- // Generate language-specific configuration
1271
- try {
1272
- const langConfig = languageSupport.generateLanguageConfig(
1273
- detectedLanguage,
1274
- '.caws/language-config.json'
1275
- );
1276
-
1277
- console.log(chalk.green('āœ… Generated language-specific configuration'));
1278
- console.log(` Language: ${langConfig.name}`);
1279
- console.log(` Tier: ${langConfig.tier}`);
1280
- console.log(
1281
- ` Thresholds: Branch ≄${langConfig.thresholds.min_branch * 100}%, Mutation ≄${langConfig.thresholds.min_mutation * 100}%`
1282
- );
1283
- } catch (langError) {
1284
- console.warn(chalk.yellow('āš ļø Could not generate language config:'), langError.message);
1285
- }
1286
- } else {
1287
- console.log(
1288
- chalk.blue('ā„¹ļø Could not detect project language - using default configuration')
1289
- );
1290
- }
1291
- }
1292
-
1293
- // Generate provenance manifest
1294
- console.log(chalk.cyan('šŸ“¦ Generating provenance manifest...'));
1295
-
1296
- const provenanceData = {
1297
- agent: 'caws-cli',
1298
- model: 'cli-interactive',
1299
- modelHash: CLI_VERSION,
1300
- toolAllowlist: [
1301
- 'node',
1302
- 'npm',
1303
- 'git',
1304
- 'fs-extra',
1305
- 'inquirer',
1306
- 'commander',
1307
- 'js-yaml',
1308
- 'ajv',
1309
- 'chalk',
1310
- ],
1311
- prompts: Object.keys(answers),
1312
- commit: null, // Will be set after git init
1313
- artifacts: ['.caws/working-spec.yaml'],
1314
- results: {
1315
- project_id: answers.projectId,
1316
- project_title: answers.projectTitle,
1317
- risk_tier: answers.riskTier,
1318
- mode: answers.projectMode,
1319
- change_budget: {
1320
- max_files: answers.maxFiles,
1321
- max_loc: answers.maxLoc,
1322
- },
1323
- },
1324
- approvals: [],
1325
- };
1326
-
1327
- // Generate provenance if tools are available
1328
- const tools = loadProvenanceTools();
1329
- if (
1330
- tools &&
1331
- typeof tools.generateProvenance === 'function' &&
1332
- typeof tools.saveProvenance === 'function'
1333
- ) {
1334
- const provenance = tools.generateProvenance(provenanceData);
1335
- await tools.saveProvenance(provenance, '.agent/provenance.json');
1336
- console.log(chalk.green('āœ… Provenance manifest generated'));
1337
- } else {
1338
- console.log(
1339
- chalk.yellow('āš ļø Provenance tools not available - skipping manifest generation')
1340
- );
1341
- }
1342
-
1343
- // Initialize git repository
1344
- if (options.git) {
1345
- try {
1346
- console.log(chalk.cyan('šŸ”§ Initializing git repository...'));
1347
-
1348
- // Check if git is available
1349
- try {
1350
- require('child_process').execSync('git --version', { stdio: 'ignore' });
1351
- } catch (error) {
1352
- console.warn(chalk.yellow('āš ļø Git not found. Skipping git initialization.'));
1353
- console.warn(chalk.blue('šŸ’” Install git to enable automatic repository setup.'));
1354
- return;
1355
- }
1356
-
1357
- require('child_process').execSync('git init', { stdio: 'inherit' });
1358
- require('child_process').execSync('git add .', { stdio: 'inherit' });
1359
- require('child_process').execSync('git commit -m "Initial CAWS project setup"', {
1360
- stdio: 'inherit',
1361
- });
1362
- console.log(chalk.green('āœ… Git repository initialized'));
1363
-
1364
- // Update provenance with commit hash
1365
- const commitHash = require('child_process')
1366
- .execSync('git rev-parse HEAD', { encoding: 'utf8' })
1367
- .trim();
1368
- const currentProvenance = JSON.parse(fs.readFileSync('.agent/provenance.json', 'utf8'));
1369
- currentProvenance.commit = commitHash;
1370
- currentProvenance.hash = require('crypto')
1371
- .createHash('sha256')
1372
- .update(JSON.stringify(currentProvenance, Object.keys(currentProvenance).sort()))
1373
- .digest('hex');
1374
- await fs.writeFile('.agent/provenance.json', JSON.stringify(currentProvenance, null, 2));
1375
-
1376
- console.log(chalk.green('āœ… Provenance updated with commit hash'));
1377
- } catch (error) {
1378
- console.warn(
1379
- chalk.yellow('āš ļø Failed to initialize git repository:'),
1380
- error?.message || String(error)
1381
- );
1382
- console.warn(chalk.blue('šŸ’” You can initialize git manually later with:'));
1383
- console.warn(" git init && git add . && git commit -m 'Initial CAWS project setup'");
1384
- }
1385
- }
1386
- } catch (error) {
1387
- console.error(
1388
- chalk.red('āŒ Error during project finalization:'),
1389
- error?.message || String(error)
1390
- );
1391
- }
1392
- }
1393
-
1394
- function continueToSuccess() {
1395
- console.log(chalk.green('\nšŸŽ‰ Project initialized successfully!'));
1396
- console.log(`šŸ“ ${chalk.cyan('Project location')}: ${path.resolve(process.cwd())}`);
1397
- console.log(chalk.bold('\nNext steps:'));
1398
- console.log('1. Customize .caws/working-spec.yaml');
1399
- console.log('2. npm install (if using Node.js)');
1400
- console.log('3. Set up your CI/CD pipeline');
1401
- console.log(chalk.blue('\nFor help: caws --help'));
1402
- }
1403
-
1404
- /**
1405
- * Scaffold existing project with CAWS components
1406
- */
1407
- async function scaffoldProject(options) {
1408
- const currentDir = process.cwd();
1409
- const projectName = path.basename(currentDir);
1410
-
1411
- console.log(chalk.cyan(`šŸ”§ Enhancing existing project with CAWS: ${projectName}`));
1412
-
1413
- try {
1414
- // Detect existing CAWS setup with current directory context
1415
- const setup = detectCAWSSetup(currentDir);
1416
-
1417
- // Preserve the original template directory from global cawsSetup
1418
- // (needed because detectCAWSSetup from within a new project won't find the template)
1419
- if (cawsSetup?.templateDir && !setup.templateDir) {
1420
- setup.templateDir = cawsSetup.templateDir;
1421
- setup.hasTemplateDir = true;
1422
- } else if (!setup.templateDir) {
1423
- // Try to find template directory using absolute paths that work in CI
1424
- const possiblePaths = [
1425
- '/home/runner/work/coding-agent-working-standard/coding-agent-working-standard/packages/caws-template',
1426
- '/workspace/packages/caws-template',
1427
- '/caws/packages/caws-template',
1428
- path.resolve(process.cwd(), '../../../packages/caws-template'),
1429
- path.resolve(process.cwd(), '../../packages/caws-template'),
1430
- path.resolve(process.cwd(), '../packages/caws-template'),
1431
- ];
1432
-
1433
- for (const testPath of possiblePaths) {
1434
- if (fs.existsSync(testPath)) {
1435
- setup.templateDir = testPath;
1436
- setup.hasTemplateDir = true;
1437
- break;
1438
- }
1439
- }
1440
-
1441
- if (!setup.templateDir) {
1442
- console.log(chalk.red(`āŒ No template directory available!`));
1443
- }
1444
- }
1445
-
1446
- // Override global cawsSetup with current context for scaffold operations
1447
- cawsSetup = setup;
1448
-
1449
- if (!setup.hasCAWSDir) {
1450
- console.error(chalk.red('āŒ No .caws directory found'));
1451
- console.error(chalk.blue('šŸ’” Run "caws init <project-name>" first to create a CAWS project'));
1452
- process.exit(1);
1453
- }
1454
-
1455
- // Adapt behavior based on setup type
1456
- if (setup.isEnhanced) {
1457
- console.log(chalk.green('šŸŽÆ Enhanced CAWS detected - adding automated publishing'));
1458
- } else if (setup.isAdvanced) {
1459
- console.log(chalk.blue('šŸ”§ Advanced CAWS detected - adding missing capabilities'));
1460
- } else {
1461
- console.log(chalk.blue('šŸ“‹ Basic CAWS detected - enhancing with additional tools'));
1462
- }
1463
-
1464
- // Generate provenance for scaffolding operation
1465
- const scaffoldProvenance = {
1466
- agent: 'caws-cli',
1467
- model: 'cli-scaffold',
1468
- modelHash: CLI_VERSION,
1469
- toolAllowlist: ['node', 'fs-extra'],
1470
- prompts: ['scaffold', options.force ? 'force' : 'normal'],
1471
- commit: null,
1472
- artifacts: [],
1473
- results: {
1474
- operation: 'scaffold',
1475
- force_mode: options.force,
1476
- target_directory: currentDir,
1477
- },
1478
- approvals: [],
1479
- timestamp: new Date().toISOString(),
1480
- version: CLI_VERSION,
1481
- };
1482
-
1483
- // Calculate hash after object is fully defined
1484
- scaffoldProvenance.hash = require('crypto')
1485
- .createHash('sha256')
1486
- .update(JSON.stringify(scaffoldProvenance))
1487
- .digest('hex');
1488
-
1489
- // Determine what enhancements to add based on setup type
1490
- const enhancements = [];
1491
-
1492
- // Add CAWS tools directory structure (matches test expectations)
1493
- enhancements.push({
1494
- name: 'apps/tools/caws',
1495
- description: 'CAWS tools directory',
1496
- required: true,
1497
- });
1498
-
1499
- enhancements.push({
1500
- name: 'codemod',
1501
- description: 'Codemod transformation scripts',
1502
- required: true,
1503
- });
1504
-
1505
- // Also add automated publishing for enhanced setups
1506
- if (setup.isEnhanced) {
1507
- enhancements.push({
1508
- name: '.github/workflows/release.yml',
1509
- description: 'GitHub Actions workflow for automated publishing',
1510
- required: true,
1511
- });
1512
-
1513
- enhancements.push({
1514
- name: '.releaserc.json',
1515
- description: 'semantic-release configuration',
1516
- required: true,
1517
- });
1518
- }
1519
-
1520
- // Add commit conventions for setups that don't have them
1521
- if (!setup.hasTemplates || !fs.existsSync(path.join(currentDir, 'COMMIT_CONVENTIONS.md'))) {
1522
- enhancements.push({
1523
- name: 'COMMIT_CONVENTIONS.md',
1524
- description: 'Commit message guidelines',
1525
- required: false,
1526
- });
1527
- }
1528
-
1529
- // Add OIDC setup guide for setups that need it
1530
- if (!setup.isEnhanced || !fs.existsSync(path.join(currentDir, 'OIDC_SETUP.md'))) {
1531
- enhancements.push({
1532
- name: 'OIDC_SETUP.md',
1533
- description: 'OIDC trusted publisher setup guide',
1534
- required: false,
1535
- });
1536
- }
1537
-
1538
- // For enhanced setups, preserve existing tools
1539
- if (setup.isEnhanced) {
1540
- console.log(chalk.blue('ā„¹ļø Preserving existing sophisticated CAWS tools'));
1541
- }
1542
-
1543
- let addedCount = 0;
1544
- let skippedCount = 0;
1545
- const addedFiles = [];
1546
-
1547
- for (const enhancement of enhancements) {
1548
- if (!setup?.templateDir) {
1549
- console.warn(
1550
- chalk.yellow(`āš ļø Template directory not available for enhancement: ${enhancement.name}`)
1551
- );
1552
- continue;
1553
- }
1554
- const sourcePath = path.join(setup.templateDir, enhancement.name);
1555
- const destPath = path.join(currentDir, enhancement.name);
1556
-
1557
- if (!fs.existsSync(destPath)) {
1558
- if (fs.existsSync(sourcePath)) {
1559
- try {
1560
- await fs.copy(sourcePath, destPath);
1561
- console.log(chalk.green(`āœ… Added ${enhancement.description}`));
1562
- addedCount++;
1563
- addedFiles.push(enhancement.name);
1564
- } catch (copyError) {
1565
- console.warn(chalk.yellow(`āš ļø Failed to add ${enhancement.name}:`), copyError.message);
1566
- }
1567
- } else {
1568
- // If source doesn't exist in template, create the directory structure
1569
- try {
1570
- await fs.ensureDir(destPath);
1571
- console.log(chalk.green(`āœ… Created ${enhancement.description}`));
1572
- addedCount++;
1573
- addedFiles.push(enhancement.name);
1574
- } catch (createError) {
1575
- console.warn(
1576
- chalk.yellow(`āš ļø Failed to create ${enhancement.name}:`),
1577
- createError.message
1578
- );
1579
- }
1580
- }
1581
- } else {
1582
- if (options.force) {
1583
- try {
1584
- await fs.remove(destPath);
1585
- if (fs.existsSync(sourcePath)) {
1586
- await fs.copy(sourcePath, destPath);
1587
- } else {
1588
- await fs.ensureDir(destPath);
1589
- }
1590
- console.log(chalk.blue(`šŸ”„ Updated ${enhancement.description}`));
1591
- addedCount++;
1592
- addedFiles.push(enhancement.name);
1593
- } catch (overwriteError) {
1594
- console.warn(
1595
- chalk.yellow(`āš ļø Failed to update ${enhancement.name}:`),
1596
- overwriteError.message
1597
- );
1598
- }
1599
- } else {
1600
- console.log(`ā­ļø Skipped ${enhancement.name} (already exists)`);
1601
- skippedCount++;
1602
- }
1603
- }
1604
- }
1605
-
1606
- // Update provenance with results
1607
- scaffoldProvenance.artifacts = addedFiles;
1608
- scaffoldProvenance.results.files_added = addedCount;
1609
- scaffoldProvenance.results.files_skipped = skippedCount;
1610
-
1611
- // Show summary
1612
- console.log(chalk.green(`\nšŸŽ‰ Enhancement completed!`));
1613
- console.log(chalk.bold(`šŸ“Š Summary: ${addedCount} added, ${skippedCount} skipped`));
1614
-
1615
- if (addedCount > 0) {
1616
- console.log(chalk.bold('\nšŸ“ Next steps:'));
1617
- console.log('1. Review the added files');
1618
- console.log('2. Set up OIDC trusted publisher (see OIDC_SETUP.md)');
1619
- console.log('3. Push to trigger automated publishing');
1620
- console.log('4. Your existing CAWS tools remain unchanged');
1621
- }
1622
-
1623
- if (setup.isEnhanced) {
1624
- console.log(
1625
- chalk.blue('\nšŸŽÆ Your enhanced CAWS setup has been improved with automated publishing!')
1626
- );
1627
- }
1628
-
1629
- if (options.force) {
1630
- console.log(chalk.yellow('\nāš ļø Force mode was used - review changes carefully'));
1631
- }
1632
-
1633
- // Save provenance manifest if tools are available
1634
- const tools = loadProvenanceTools();
1635
- if (tools && typeof tools.saveProvenance === 'function') {
1636
- await tools.saveProvenance(scaffoldProvenance, '.agent/scaffold-provenance.json');
1637
- console.log(chalk.green('āœ… Scaffolding provenance saved'));
1638
- } else {
1639
- console.log(chalk.yellow('āš ļø Provenance tools not available - skipping manifest save'));
1640
- }
1641
- } catch (error) {
1642
- // Handle circular reference errors from Commander.js
1643
- if (error.message && error.message.includes('Converting circular structure to JSON')) {
1644
- console.log(
1645
- chalk.yellow('āš ļø Scaffolding completed with minor issues (circular reference handled)')
1646
- );
1647
- console.log(chalk.green('āœ… CAWS components scaffolded successfully'));
1648
- } else {
1649
- console.error(chalk.red('āŒ Error during scaffolding:'), error.message);
1650
- process.exit(1);
1651
- }
1652
- }
1653
- }
60
+ // Set up dependencies for modules that need them
61
+ setScaffoldDependencies({
62
+ cawsSetup,
63
+ loadProvenanceTools,
64
+ });
1654
65
 
1655
- /**
1656
- * Show version information
1657
- */
1658
- // function showVersion() {
1659
- // console.log(chalk.bold(`CAWS CLI v${CLI_VERSION}`));
1660
- // console.log(chalk.cyan('Coding Agent Workflow System - Scaffolding Tool'));
1661
- // console.log(chalk.gray('Author: @darianrosebrook'));
1662
- // console.log(chalk.gray('License: MIT'));
1663
- // }
66
+ setFinalizationDependencies({
67
+ languageSupport,
68
+ loadProvenanceTools,
69
+ });
1664
70
 
1665
- // CLI Commands
1666
- program
1667
- .name('caws')
1668
- .description('CAWS - Coding Agent Workflow System CLI')
1669
- .version(CLI_VERSION, '-v, --version', 'Show version information');
71
+ // Setup CLI program
72
+ program.name('caws').description('CAWS - Coding Agent Workflow System CLI').version(CLI_VERSION);
1670
73
 
74
+ // Init command
1671
75
  program
1672
76
  .command('init')
1673
- .alias('i')
1674
77
  .description('Initialize a new project with CAWS')
1675
- .argument('<project-name>', 'Name of the new project')
1676
- .option('-i, --interactive', 'Run interactive setup', true)
1677
- .option('-g, --git', 'Initialize git repository', true)
1678
- .option('-n, --non-interactive', 'Skip interactive prompts')
1679
- .option('--no-git', "Don't initialize git repository")
78
+ .argument('[project-name]', 'Name of the project to create (use "." for current directory)')
79
+ .option('-i, --interactive', 'Run interactive setup wizard', true)
80
+ .option('--non-interactive', 'Skip interactive prompts (use defaults)', false)
81
+ .option('--template <template>', 'Use specific project template')
1680
82
  .action(initProject);
1681
83
 
84
+ // Scaffold command
1682
85
  program
1683
86
  .command('scaffold')
1684
- .alias('s')
1685
87
  .description('Add CAWS components to existing project')
1686
- .option('-f, --force', 'Overwrite existing files')
88
+ .option('-f, --force', 'Overwrite existing files', false)
89
+ .option('--minimal', 'Only essential components', false)
90
+ .option('--with-codemods', 'Include codemod scripts', false)
91
+ .option('--with-oidc', 'Include OIDC trusted publisher setup', false)
1687
92
  .action(scaffoldProject);
1688
93
 
94
+ // Validate command
95
+ program
96
+ .command('validate')
97
+ .description('Validate CAWS working spec with suggestions')
98
+ .argument('[spec-file]', 'Path to working spec file (default: .caws/working-spec.yaml)')
99
+ .option('-q, --quiet', 'Suppress suggestions and warnings', false)
100
+ .option('--auto-fix', 'Automatically fix safe validation issues', false)
101
+ .action(validateCommand);
102
+
103
+ // Tool command
104
+ program
105
+ .command('tool')
106
+ .description('Execute CAWS tools programmatically')
107
+ .argument('<tool-id>', 'ID of the tool to execute')
108
+ .option('-p, --params <json>', 'Parameters as JSON string', '{}')
109
+ .option('-t, --timeout <ms>', 'Execution timeout in milliseconds', parseInt, 30000)
110
+ .action(executeTool);
111
+
112
+ // Test Analysis command
113
+ program
114
+ .command('test-analysis <subcommand> [options...]')
115
+ .description('Statistical analysis for budget prediction and test optimization')
116
+ .action(testAnalysisCommand);
117
+
118
+ // Provenance command
119
+ program
120
+ .command('provenance')
121
+ .description('Manage CAWS provenance tracking and audit trails')
122
+ .argument('<subcommand>', 'Command: update, show, verify, analyze-ai')
123
+ .option('-c, --commit <hash>', 'Git commit hash')
124
+ .option('-m, --message <msg>', 'Commit message')
125
+ .option('-a, --author <info>', 'Author information')
126
+ .option('-q, --quiet', 'Suppress output')
127
+ .option('-o, --output <path>', 'Output path for provenance files')
128
+ .action(provenanceCommand);
129
+
1689
130
  // Error handling
1690
131
  program.exitOverride((err) => {
1691
132
  if (
@@ -1699,7 +140,7 @@ program.exitOverride((err) => {
1699
140
  process.exit(1);
1700
141
  });
1701
142
 
1702
- // Parse and run (only when run directly, not when required as module)
143
+ // Parse and run
1703
144
  if (require.main === module) {
1704
145
  try {
1705
146
  program.parse();