@cxtms/cx-schema 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +384 -0
  2. package/dist/cli.d.ts +6 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +1523 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +11 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/types.d.ts +111 -0
  11. package/dist/types.d.ts.map +1 -0
  12. package/dist/types.js +6 -0
  13. package/dist/types.js.map +1 -0
  14. package/dist/utils/schemaLoader.d.ts +17 -0
  15. package/dist/utils/schemaLoader.d.ts.map +1 -0
  16. package/dist/utils/schemaLoader.js +134 -0
  17. package/dist/utils/schemaLoader.js.map +1 -0
  18. package/dist/validator.d.ts +64 -0
  19. package/dist/validator.d.ts.map +1 -0
  20. package/dist/validator.js +380 -0
  21. package/dist/validator.js.map +1 -0
  22. package/dist/workflowValidator.d.ts +64 -0
  23. package/dist/workflowValidator.d.ts.map +1 -0
  24. package/dist/workflowValidator.js +410 -0
  25. package/dist/workflowValidator.js.map +1 -0
  26. package/package.json +50 -0
  27. package/schemas/actions/all.json +26 -0
  28. package/schemas/actions/confirm.json +21 -0
  29. package/schemas/actions/consoleLog.json +16 -0
  30. package/schemas/actions/dialog.json +25 -0
  31. package/schemas/actions/fileDownload.json +16 -0
  32. package/schemas/actions/forEach.json +31 -0
  33. package/schemas/actions/if.json +12 -0
  34. package/schemas/actions/mutation.json +25 -0
  35. package/schemas/actions/navigate.json +18 -0
  36. package/schemas/actions/navigateBack.json +22 -0
  37. package/schemas/actions/navigateBackOrClose.json +21 -0
  38. package/schemas/actions/notification.json +19 -0
  39. package/schemas/actions/openBarcodeScanner.json +104 -0
  40. package/schemas/actions/query.json +32 -0
  41. package/schemas/actions/refresh.json +13 -0
  42. package/schemas/actions/resetDirtyState.json +22 -0
  43. package/schemas/actions/setFields.json +21 -0
  44. package/schemas/actions/setStore.json +13 -0
  45. package/schemas/actions/validateForm.json +15 -0
  46. package/schemas/actions/workflow.json +24 -0
  47. package/schemas/components/README.md +147 -0
  48. package/schemas/components/appComponent.json +50 -0
  49. package/schemas/components/barcodeScanner.json +69 -0
  50. package/schemas/components/button.json +123 -0
  51. package/schemas/components/calendar.json +489 -0
  52. package/schemas/components/card.json +176 -0
  53. package/schemas/components/collection.json +54 -0
  54. package/schemas/components/dataGrid.json +119 -0
  55. package/schemas/components/datasource.json +151 -0
  56. package/schemas/components/dropdown.json +57 -0
  57. package/schemas/components/field-collection.json +618 -0
  58. package/schemas/components/field.json +265 -0
  59. package/schemas/components/form.json +234 -0
  60. package/schemas/components/index.json +68 -0
  61. package/schemas/components/layout.json +69 -0
  62. package/schemas/components/module.json +138 -0
  63. package/schemas/components/navDropdown.json +36 -0
  64. package/schemas/components/navbar.json +78 -0
  65. package/schemas/components/navbarItem.json +28 -0
  66. package/schemas/components/navbarLink.json +36 -0
  67. package/schemas/components/row.json +31 -0
  68. package/schemas/components/tab.json +34 -0
  69. package/schemas/components/tabs.json +35 -0
  70. package/schemas/components/timeline.json +172 -0
  71. package/schemas/components/timelineGrid.json +324 -0
  72. package/schemas/fields/README.md +66 -0
  73. package/schemas/fields/attachment.json +156 -0
  74. package/schemas/fields/autocomplete-googleplaces.json +130 -0
  75. package/schemas/fields/checkbox.json +82 -0
  76. package/schemas/fields/date.json +88 -0
  77. package/schemas/fields/datetime.json +75 -0
  78. package/schemas/fields/email.json +75 -0
  79. package/schemas/fields/index.json +53 -0
  80. package/schemas/fields/number.json +91 -0
  81. package/schemas/fields/password.json +70 -0
  82. package/schemas/fields/radio.json +94 -0
  83. package/schemas/fields/rangedatetime.json +56 -0
  84. package/schemas/fields/select-async.json +334 -0
  85. package/schemas/fields/select.json +115 -0
  86. package/schemas/fields/tel.json +79 -0
  87. package/schemas/fields/text.json +86 -0
  88. package/schemas/fields/textarea.json +95 -0
  89. package/schemas/fields/time.json +91 -0
  90. package/schemas/fields/url.json +74 -0
  91. package/schemas/schema.graphql +10492 -0
  92. package/schemas/schemas.json +598 -0
  93. package/schemas/workflows/activity.json +111 -0
  94. package/schemas/workflows/common/condition.json +48 -0
  95. package/schemas/workflows/common/expression.json +76 -0
  96. package/schemas/workflows/common/mapping.json +134 -0
  97. package/schemas/workflows/input.json +76 -0
  98. package/schemas/workflows/output.json +41 -0
  99. package/schemas/workflows/schedule.json +26 -0
  100. package/schemas/workflows/tasks/accounting-transaction.json +95 -0
  101. package/schemas/workflows/tasks/all.json +34 -0
  102. package/schemas/workflows/tasks/attachment.json +94 -0
  103. package/schemas/workflows/tasks/charge.json +90 -0
  104. package/schemas/workflows/tasks/commodity.json +89 -0
  105. package/schemas/workflows/tasks/contact.json +82 -0
  106. package/schemas/workflows/tasks/csv.json +79 -0
  107. package/schemas/workflows/tasks/document-render.json +105 -0
  108. package/schemas/workflows/tasks/document-send.json +84 -0
  109. package/schemas/workflows/tasks/email-send.json +110 -0
  110. package/schemas/workflows/tasks/error.json +72 -0
  111. package/schemas/workflows/tasks/export.json +90 -0
  112. package/schemas/workflows/tasks/foreach.json +69 -0
  113. package/schemas/workflows/tasks/generic.json +47 -0
  114. package/schemas/workflows/tasks/graphql.json +78 -0
  115. package/schemas/workflows/tasks/httpRequest.json +119 -0
  116. package/schemas/workflows/tasks/job.json +88 -0
  117. package/schemas/workflows/tasks/log.json +73 -0
  118. package/schemas/workflows/tasks/map.json +58 -0
  119. package/schemas/workflows/tasks/order.json +87 -0
  120. package/schemas/workflows/tasks/payment.json +85 -0
  121. package/schemas/workflows/tasks/setVariable.json +76 -0
  122. package/schemas/workflows/tasks/switch.json +75 -0
  123. package/schemas/workflows/tasks/template.json +73 -0
  124. package/schemas/workflows/tasks/validation.json +90 -0
  125. package/schemas/workflows/tasks/while.json +53 -0
  126. package/schemas/workflows/tasks/workflow-execute.json +82 -0
  127. package/schemas/workflows/trigger.json +86 -0
  128. package/schemas/workflows/variable.json +46 -0
  129. package/schemas/workflows/workflow.json +172 -0
  130. package/scripts/postinstall.js +161 -0
  131. package/scripts/setup-vscode.js +80 -0
  132. package/templates/module.yaml +83 -0
  133. package/templates/workflow.yaml +100 -0
package/dist/cli.js ADDED
@@ -0,0 +1,1523 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * CX Schema Validator CLI - Unified validation for YAML modules and workflows
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const chalk_1 = __importDefault(require("chalk"));
46
+ const yaml = __importStar(require("js-yaml"));
47
+ const validator_1 = require("./validator");
48
+ const workflowValidator_1 = require("./workflowValidator");
49
+ // ============================================================================
50
+ // Constants
51
+ // ============================================================================
52
+ const VERSION = require('../package.json').version;
53
+ const PROGRAM_NAME = 'cx-validate';
54
+ // ============================================================================
55
+ // Help Text
56
+ // ============================================================================
57
+ const HELP_TEXT = `
58
+ ${chalk_1.default.bold.cyan('╔═══════════════════════════════════════════════════════════════════════════╗')}
59
+ ${chalk_1.default.bold.cyan('║')} ${chalk_1.default.bold.white('CX SCHEMA VALIDATOR')} ${chalk_1.default.bold.cyan('║')}
60
+ ${chalk_1.default.bold.cyan('║')} ${chalk_1.default.gray('Unified validation for CargoXplorer YAML files')} ${chalk_1.default.bold.cyan('║')}
61
+ ${chalk_1.default.bold.cyan('╚═══════════════════════════════════════════════════════════════════════════╝')}
62
+
63
+ ${chalk_1.default.bold.yellow('DESCRIPTION:')}
64
+ Validates CargoXplorer YAML module and workflow files against JSON Schema
65
+ definitions. Provides detailed error feedback with examples and schema
66
+ references to help fix validation issues.
67
+
68
+ ${chalk_1.default.bold.yellow('USAGE:')}
69
+ ${chalk_1.default.cyan(PROGRAM_NAME)} [command] [options] <files...>
70
+
71
+ ${chalk_1.default.bold.yellow('COMMANDS:')}
72
+ ${chalk_1.default.green('validate')} Validate YAML file(s) ${chalk_1.default.gray('(default command)')}
73
+ ${chalk_1.default.green('report')} Generate validation report for multiple files
74
+ ${chalk_1.default.green('init')} Initialize a new CX project with app.yaml, README.md, AGENTS.md
75
+ ${chalk_1.default.green('create')} Create a new module or workflow from template
76
+ ${chalk_1.default.green('schema')} Show JSON schema for a component or task
77
+ ${chalk_1.default.green('example')} Show example YAML for a component or task
78
+ ${chalk_1.default.green('list')} List available schemas (modules, workflows, tasks)
79
+ ${chalk_1.default.green('help')} Show this help message
80
+
81
+ ${chalk_1.default.bold.yellow('OPTIONS:')}
82
+ ${chalk_1.default.green('-h, --help')} Show this help message
83
+ ${chalk_1.default.green('-v, --version')} Show version number
84
+ ${chalk_1.default.green('-t, --type <type>')} Validation type: ${chalk_1.default.cyan('module')}, ${chalk_1.default.cyan('workflow')}, or ${chalk_1.default.cyan('auto')} ${chalk_1.default.gray('(default: auto)')}
85
+ ${chalk_1.default.green('-f, --format <format>')} Output format: ${chalk_1.default.cyan('pretty')}, ${chalk_1.default.cyan('json')}, or ${chalk_1.default.cyan('compact')} ${chalk_1.default.gray('(default: pretty)')}
86
+ ${chalk_1.default.green('-s, --schemas <path>')} Path to schemas directory
87
+ ${chalk_1.default.green('--verbose')} Show detailed output with schema paths
88
+ ${chalk_1.default.green('--quiet')} Only show errors, suppress other output
89
+ ${chalk_1.default.green('-r, --report <file>')} Generate report to file (html, md, or json)
90
+ ${chalk_1.default.green('--report-format <fmt>')} Report format: ${chalk_1.default.cyan('html')}, ${chalk_1.default.cyan('markdown')}, or ${chalk_1.default.cyan('json')} ${chalk_1.default.gray('(default: auto from extension)')}
91
+
92
+ ${chalk_1.default.bold.yellow('VALIDATION EXAMPLES:')}
93
+ ${chalk_1.default.gray('# Validate a module YAML file')}
94
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} modules/countries-module.yaml`)}
95
+
96
+ ${chalk_1.default.gray('# Validate a workflow YAML file')}
97
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} workflows/my-workflow.yaml`)}
98
+
99
+ ${chalk_1.default.gray('# Auto-detect file type and validate')}
100
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} --type auto my-file.yaml`)}
101
+
102
+ ${chalk_1.default.gray('# Validate with custom schemas directory')}
103
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} --schemas ./custom-schemas file.yaml`)}
104
+
105
+ ${chalk_1.default.gray('# Output validation results as JSON')}
106
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} --format json file.yaml > results.json`)}
107
+
108
+ ${chalk_1.default.gray('# Validate multiple files')}
109
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} module1.yaml module2.yaml workflow1.yaml`)}
110
+
111
+ ${chalk_1.default.bold.yellow('PROJECT COMMANDS:')}
112
+ ${chalk_1.default.gray('# Initialize a new project')}
113
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} init`)}
114
+
115
+ ${chalk_1.default.gray('# Create a new module from template')}
116
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} create module my-module`)}
117
+
118
+ ${chalk_1.default.gray('# Create a new workflow from template')}
119
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} create workflow my-workflow`)}
120
+
121
+ ${chalk_1.default.bold.yellow('SCHEMA COMMANDS:')}
122
+ ${chalk_1.default.gray('# Show schema for a component')}
123
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} schema form`)}
124
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} schema dataGrid`)}
125
+
126
+ ${chalk_1.default.gray('# Show schema for a workflow task')}
127
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} schema foreach`)}
128
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} schema graphql`)}
129
+
130
+ ${chalk_1.default.gray('# Show example YAML for a component')}
131
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} example form`)}
132
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} example workflow`)}
133
+
134
+ ${chalk_1.default.gray('# List all available schemas')}
135
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} list`)}
136
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} list --type workflow`)}
137
+
138
+ ${chalk_1.default.bold.yellow('VALIDATION TYPES:')}
139
+ ${chalk_1.default.bold('module')} - CargoXplorer UI module definitions (components, routes, entities)
140
+ ${chalk_1.default.bold('workflow')} - CargoXplorer workflow definitions (activities, tasks, triggers)
141
+ ${chalk_1.default.bold('auto')} - Auto-detect based on file content (checks for 'workflow:' vs 'module:')
142
+
143
+ ${chalk_1.default.bold.yellow('OUTPUT FORMATS:')}
144
+ ${chalk_1.default.bold('pretty')} - Colorized, human-readable output with detailed error info
145
+ ${chalk_1.default.bold('json')} - JSON output suitable for CI/CD pipelines
146
+ ${chalk_1.default.bold('compact')} - Minimal output showing only pass/fail and error count
147
+
148
+ ${chalk_1.default.bold.yellow('EXIT CODES:')}
149
+ ${chalk_1.default.green('0')} - Validation passed (no errors)
150
+ ${chalk_1.default.red('1')} - Validation failed (errors found)
151
+ ${chalk_1.default.red('2')} - CLI error (invalid arguments, file not found, etc.)
152
+
153
+ ${chalk_1.default.bold.yellow('ENVIRONMENT VARIABLES:')}
154
+ ${chalk_1.default.green('CX_SCHEMA_PATH')} - Default path to schemas directory
155
+ ${chalk_1.default.green('NO_COLOR')} - Disable colored output
156
+
157
+ ${chalk_1.default.bold.yellow('MORE INFORMATION:')}
158
+ Documentation: ${chalk_1.default.underline.cyan('https://github.com/cxtms/cx-schema')}
159
+ Report issues: ${chalk_1.default.underline.cyan('https://github.com/cxtms/cx-schema/issues')}
160
+ `;
161
+ const SCHEMA_HELP = `
162
+ ${chalk_1.default.bold.yellow('SCHEMA COMMAND')}
163
+
164
+ Show the JSON Schema definition for a component, field, action, or task.
165
+
166
+ ${chalk_1.default.bold.yellow('USAGE:')}
167
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} schema <name>`)}
168
+
169
+ ${chalk_1.default.bold.yellow('AVAILABLE SCHEMAS:')}
170
+
171
+ ${chalk_1.default.bold('Module Components:')}
172
+ form, dataGrid, layout, tabs, tab, field, button, collection,
173
+ dropdown, datasource, calendar, card, navbar, timeline
174
+
175
+ ${chalk_1.default.bold('Workflow Core:')}
176
+ workflow, activity, input, output, variable, trigger, schedule
177
+
178
+ ${chalk_1.default.bold('Workflow Tasks:')}
179
+ foreach, switch, while, validation, graphql, httpRequest,
180
+ setVariable, map, log, error, csv, export, template,
181
+ order, contact, commodity, job, attachment,
182
+ email-send, document-render, charge, workflow-execute
183
+
184
+ ${chalk_1.default.bold.yellow('EXAMPLES:')}
185
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} schema form`)}
186
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} schema foreach`)}
187
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} schema workflow`)}
188
+ `;
189
+ const LIST_HELP = `
190
+ ${chalk_1.default.bold.yellow('LIST COMMAND')}
191
+
192
+ List all available schemas for validation.
193
+
194
+ ${chalk_1.default.bold.yellow('USAGE:')}
195
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} list [options]`)}
196
+
197
+ ${chalk_1.default.bold.yellow('OPTIONS:')}
198
+ ${chalk_1.default.green('--type <type>')} Filter by type: module, workflow, or all ${chalk_1.default.gray('(default: all)')}
199
+
200
+ ${chalk_1.default.bold.yellow('EXAMPLES:')}
201
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} list`)}
202
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} list --type module`)}
203
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} list --type workflow`)}
204
+ `;
205
+ const INIT_HELP = `
206
+ ${chalk_1.default.bold.yellow('INIT COMMAND')}
207
+
208
+ Initialize a new CX project with configuration files.
209
+
210
+ ${chalk_1.default.bold.yellow('USAGE:')}
211
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} init`)}
212
+
213
+ ${chalk_1.default.bold.yellow('FILES CREATED:')}
214
+ ${chalk_1.default.green('app.yaml')} - Project configuration
215
+ ${chalk_1.default.green('README.md')} - Project documentation
216
+ ${chalk_1.default.green('AGENTS.md')} - AI assistant instructions for validation
217
+ `;
218
+ const CREATE_HELP = `
219
+ ${chalk_1.default.bold.yellow('CREATE COMMAND')}
220
+
221
+ Create a new module or workflow from template.
222
+
223
+ ${chalk_1.default.bold.yellow('USAGE:')}
224
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} create <type> <name>`)}
225
+
226
+ ${chalk_1.default.bold.yellow('TYPES:')}
227
+ ${chalk_1.default.green('module')} - Create a new UI module YAML file
228
+ ${chalk_1.default.green('workflow')} - Create a new workflow YAML file
229
+
230
+ ${chalk_1.default.bold.yellow('EXAMPLES:')}
231
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} create module orders`)}
232
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} create workflow invoice-generator`)}
233
+ `;
234
+ // ============================================================================
235
+ // Templates
236
+ // ============================================================================
237
+ function generateAppYaml() {
238
+ return `# CargoXplorer Application Configuration
239
+ # Generated by cx-validate init
240
+
241
+ app:
242
+ name: "My CX Application"
243
+ version: "1.0.0"
244
+ description: "CargoXplorer application"
245
+
246
+ # Module directories
247
+ modules:
248
+ - path: "./modules"
249
+ pattern: "*.yaml"
250
+
251
+ # Workflow directories
252
+ workflows:
253
+ - path: "./workflows"
254
+ pattern: "*.yaml"
255
+
256
+ # Validation settings
257
+ validation:
258
+ strict: true
259
+ failOnWarnings: false
260
+ `;
261
+ }
262
+ function generateReadme() {
263
+ return `# CargoXplorer Application
264
+
265
+ This project contains CargoXplorer modules and workflows.
266
+
267
+ ## Project Structure
268
+
269
+ \`\`\`
270
+ ├── app.yaml # Application configuration
271
+ ├── modules/ # UI module definitions
272
+ │ └── *.yaml
273
+ ├── workflows/ # Workflow definitions
274
+ │ └── *.yaml
275
+ ├── README.md # This file
276
+ └── AGENTS.md # AI assistant instructions
277
+ \`\`\`
278
+
279
+ ## Validation
280
+
281
+ ### Install the validator
282
+
283
+ \`\`\`bash
284
+ npm install @cxtms/cx-schema
285
+ \`\`\`
286
+
287
+ ### Validate files
288
+
289
+ \`\`\`bash
290
+ # Validate all modules
291
+ npx cx-validate modules/*.yaml
292
+
293
+ # Validate all workflows
294
+ npx cx-validate workflows/*.yaml
295
+
296
+ # Validate with detailed output
297
+ npx cx-validate --verbose modules/my-module.yaml
298
+
299
+ # Generate validation report
300
+ npx cx-validate report modules/*.yaml workflows/*.yaml --report report.html
301
+ \`\`\`
302
+
303
+ ### Create new files
304
+
305
+ \`\`\`bash
306
+ # Create a new module
307
+ npx cx-validate create module my-module
308
+
309
+ # Create a new workflow
310
+ npx cx-validate create workflow my-workflow
311
+ \`\`\`
312
+
313
+ ### View schemas and examples
314
+
315
+ \`\`\`bash
316
+ # List available schemas
317
+ npx cx-validate list
318
+
319
+ # View schema for a component
320
+ npx cx-validate schema form
321
+
322
+ # View example YAML
323
+ npx cx-validate example workflow
324
+ \`\`\`
325
+
326
+ ## Documentation
327
+
328
+ - [CX Schema CLI Documentation](https://docs.cargoxplorer.com/docs/documents/cx-schema-cli)
329
+ - [Module Development Guide](https://docs.cargoxplorer.com/docs/development/app-modules)
330
+ - [Workflow Development Guide](https://docs.cargoxplorer.com/docs/development/workflows)
331
+ `;
332
+ }
333
+ function generateAgentsMd() {
334
+ return `# AI Assistant Instructions for CargoXplorer Development
335
+
336
+ This file provides instructions for AI assistants (like Claude, GPT, Copilot) when working with this CargoXplorer project.
337
+
338
+ ## Validation Commands
339
+
340
+ When making changes to YAML files, always validate them:
341
+
342
+ \`\`\`bash
343
+ # Validate a specific module file
344
+ npx cx-validate modules/<module-name>.yaml
345
+
346
+ # Validate a specific workflow file
347
+ npx cx-validate workflows/<workflow-name>.yaml
348
+
349
+ # Validate all files with a report
350
+ npx cx-validate report modules/*.yaml workflows/*.yaml --report validation-report.md
351
+ \`\`\`
352
+
353
+ ## Schema Reference
354
+
355
+ Before editing components or tasks, check the schema:
356
+
357
+ \`\`\`bash
358
+ # View schema for components
359
+ npx cx-validate schema form
360
+ npx cx-validate schema dataGrid
361
+ npx cx-validate schema layout
362
+
363
+ # View schema for workflow tasks
364
+ npx cx-validate schema foreach
365
+ npx cx-validate schema graphql
366
+ npx cx-validate schema switch
367
+ \`\`\`
368
+
369
+ ## Creating New Files
370
+
371
+ Use templates to create properly structured files:
372
+
373
+ \`\`\`bash
374
+ # Create a new module
375
+ npx cx-validate create module <name>
376
+
377
+ # Create a new workflow
378
+ npx cx-validate create workflow <name>
379
+ \`\`\`
380
+
381
+ ## Module Structure
382
+
383
+ Modules contain UI component definitions:
384
+
385
+ - **Components**: form, dataGrid, layout, tabs, card, etc.
386
+ - **Fields**: text, number, select, date, checkbox, etc.
387
+ - **Actions**: navigate, mutation, query, setFields, etc.
388
+ - **Routes**: Define navigation paths
389
+
390
+ ## Workflow Structure
391
+
392
+ Workflows contain automation definitions:
393
+
394
+ - **workflow**: Metadata (workflowId, name, executionMode)
395
+ - **inputs/outputs**: Parameter definitions
396
+ - **variables**: Internal state
397
+ - **activities**: Ordered steps containing tasks
398
+ - **triggers**: Manual, Entity, or Scheduled triggers
399
+
400
+ ### Common Task Types
401
+
402
+ - **Control flow**: foreach, switch, while, validation
403
+ - **Data**: Query/GraphQL, Map@1, SetVariable@1
404
+ - **Entity operations**: Order/Create@1, Contact/Update@1, etc.
405
+ - **Communication**: Email/Send@1, Document/Render@1
406
+
407
+ ## Best Practices
408
+
409
+ 1. **Always validate** after making changes to YAML files
410
+ 2. **Use verbose mode** (\`--verbose\`) for detailed error information
411
+ 3. **Check schemas** before adding new properties
412
+ 4. **Use templates** when creating new files
413
+ 5. **Generate reports** for batch validation of multiple files
414
+ `;
415
+ }
416
+ function findTemplatesPath() {
417
+ // Check for templates in node_modules
418
+ const nodeModulesTemplates = path.join(process.cwd(), 'node_modules', '@cxtms/cx-schema', 'templates');
419
+ if (fs.existsSync(nodeModulesTemplates)) {
420
+ return nodeModulesTemplates;
421
+ }
422
+ // Check in package directory (for development)
423
+ const packageTemplates = path.join(__dirname, '../templates');
424
+ if (fs.existsSync(packageTemplates)) {
425
+ return packageTemplates;
426
+ }
427
+ return undefined;
428
+ }
429
+ function loadTemplate(templateName) {
430
+ const templatesPath = findTemplatesPath();
431
+ if (!templatesPath) {
432
+ throw new Error('Could not find templates directory');
433
+ }
434
+ const templateFile = path.join(templatesPath, `${templateName}.yaml`);
435
+ if (!fs.existsSync(templateFile)) {
436
+ throw new Error(`Template not found: ${templateName}`);
437
+ }
438
+ return fs.readFileSync(templateFile, 'utf-8');
439
+ }
440
+ function processTemplate(template, variables) {
441
+ let result = template;
442
+ // Replace all {{variableName}} placeholders (but not \{{...}} which are escaped)
443
+ for (const [key, value] of Object.entries(variables)) {
444
+ // Match {{key}} but not \{{key}}
445
+ const regex = new RegExp(`(?<!\\\\)\\{\\{${key}\\}\\}`, 'g');
446
+ result = result.replace(regex, value);
447
+ }
448
+ // Unescape \{{ to {{ (for runtime expressions like {{inputs.entityId}})
449
+ result = result.replace(/\\(\{\{)/g, '$1');
450
+ return result;
451
+ }
452
+ function generateTemplateContent(type, name, fileName) {
453
+ const template = loadTemplate(type);
454
+ const displayName = name
455
+ .split('-')
456
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
457
+ .join(' ');
458
+ const variables = {
459
+ name,
460
+ displayName,
461
+ displayNameNoSpaces: displayName.replace(/\s/g, ''),
462
+ uuid: generateUUID(),
463
+ fileName
464
+ };
465
+ return processTemplate(template, variables);
466
+ }
467
+ function generateUUID() {
468
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
469
+ const r = Math.random() * 16 | 0;
470
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
471
+ return v.toString(16);
472
+ });
473
+ }
474
+ // ============================================================================
475
+ // Init and Create Commands
476
+ // ============================================================================
477
+ function runInit() {
478
+ console.log(chalk_1.default.bold.cyan('\n╔═══════════════════════════════════════════════════════════════════╗'));
479
+ console.log(chalk_1.default.bold.cyan('║ CX PROJECT INITIALIZATION ║'));
480
+ console.log(chalk_1.default.bold.cyan('╚═══════════════════════════════════════════════════════════════════╝\n'));
481
+ const files = [
482
+ { name: 'app.yaml', content: generateAppYaml() },
483
+ { name: 'README.md', content: generateReadme() },
484
+ { name: 'AGENTS.md', content: generateAgentsMd() }
485
+ ];
486
+ const createdDirs = [];
487
+ const createdFiles = [];
488
+ const skippedFiles = [];
489
+ // Create directories
490
+ for (const dir of ['modules', 'workflows']) {
491
+ const dirPath = path.join(process.cwd(), dir);
492
+ if (!fs.existsSync(dirPath)) {
493
+ fs.mkdirSync(dirPath, { recursive: true });
494
+ createdDirs.push(dir);
495
+ }
496
+ }
497
+ // Create files
498
+ for (const file of files) {
499
+ const filePath = path.join(process.cwd(), file.name);
500
+ if (fs.existsSync(filePath)) {
501
+ skippedFiles.push(file.name);
502
+ }
503
+ else {
504
+ fs.writeFileSync(filePath, file.content, 'utf-8');
505
+ createdFiles.push(file.name);
506
+ }
507
+ }
508
+ // Output summary
509
+ if (createdDirs.length > 0) {
510
+ console.log(chalk_1.default.bold(' Created directories:'));
511
+ for (const dir of createdDirs) {
512
+ console.log(chalk_1.default.green(` ✓ ${dir}/`));
513
+ }
514
+ console.log('');
515
+ }
516
+ if (createdFiles.length > 0) {
517
+ console.log(chalk_1.default.bold(' Created files:'));
518
+ for (const file of createdFiles) {
519
+ console.log(chalk_1.default.green(` ✓ ${file}`));
520
+ }
521
+ console.log('');
522
+ }
523
+ if (skippedFiles.length > 0) {
524
+ console.log(chalk_1.default.bold(' Skipped (already exist):'));
525
+ for (const file of skippedFiles) {
526
+ console.log(chalk_1.default.yellow(` - ${file}`));
527
+ }
528
+ console.log('');
529
+ }
530
+ console.log(chalk_1.default.gray(' Next steps:'));
531
+ console.log(chalk_1.default.gray(` 1. Edit ${chalk_1.default.white('app.yaml')} to configure your project`));
532
+ console.log(chalk_1.default.gray(` 2. Create modules: ${chalk_1.default.white(`${PROGRAM_NAME} create module <name>`)}`));
533
+ console.log(chalk_1.default.gray(` 3. Create workflows: ${chalk_1.default.white(`${PROGRAM_NAME} create workflow <name>`)}`));
534
+ console.log(chalk_1.default.gray(` 4. Validate files: ${chalk_1.default.white(`${PROGRAM_NAME} modules/*.yaml`)}`));
535
+ console.log('');
536
+ }
537
+ function runCreate(type, name) {
538
+ if (!type || !['module', 'workflow'].includes(type)) {
539
+ console.error(chalk_1.default.red('Error: Invalid or missing type. Use: module or workflow'));
540
+ console.error(chalk_1.default.gray(`Example: ${PROGRAM_NAME} create module my-module`));
541
+ process.exit(2);
542
+ }
543
+ if (!name) {
544
+ console.error(chalk_1.default.red(`Error: Missing name for ${type}`));
545
+ console.error(chalk_1.default.gray(`Example: ${PROGRAM_NAME} create ${type} my-${type}`));
546
+ process.exit(2);
547
+ }
548
+ // Sanitize name
549
+ const safeName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
550
+ // Determine output directory and file
551
+ const dir = type === 'module' ? 'modules' : 'workflows';
552
+ const fileName = `${safeName}.yaml`;
553
+ const filePath = path.join(process.cwd(), dir, fileName);
554
+ // Check if file already exists
555
+ if (fs.existsSync(filePath)) {
556
+ console.error(chalk_1.default.red(`Error: File already exists: ${filePath}`));
557
+ process.exit(2);
558
+ }
559
+ // Create directory if needed
560
+ const dirPath = path.join(process.cwd(), dir);
561
+ if (!fs.existsSync(dirPath)) {
562
+ fs.mkdirSync(dirPath, { recursive: true });
563
+ }
564
+ // Generate content from template
565
+ const relativeFileName = path.join(dir, fileName);
566
+ let content;
567
+ try {
568
+ content = generateTemplateContent(type, safeName, relativeFileName);
569
+ }
570
+ catch (error) {
571
+ console.error(chalk_1.default.red(`Error loading template: ${error.message}`));
572
+ process.exit(2);
573
+ }
574
+ // Write file
575
+ fs.writeFileSync(filePath, content, 'utf-8');
576
+ console.log(chalk_1.default.green(`\n✓ Created ${type}: ${path.join(dir, fileName)}`));
577
+ console.log(chalk_1.default.gray(`\n Next steps:`));
578
+ console.log(chalk_1.default.gray(` 1. Edit ${chalk_1.default.white(filePath)} to customize`));
579
+ console.log(chalk_1.default.gray(` 2. Validate: ${chalk_1.default.white(`${PROGRAM_NAME} ${filePath}`)}`));
580
+ console.log(chalk_1.default.gray(` 3. View schema: ${chalk_1.default.white(`${PROGRAM_NAME} schema ${type}`)}`));
581
+ console.log('');
582
+ }
583
+ // ============================================================================
584
+ // Argument Parsing
585
+ // ============================================================================
586
+ function parseArgs(args) {
587
+ const files = [];
588
+ let command = null;
589
+ const options = {
590
+ help: false,
591
+ version: false,
592
+ type: 'auto',
593
+ format: 'pretty',
594
+ verbose: false,
595
+ showSchema: null,
596
+ showExample: null,
597
+ listSchemas: false,
598
+ listTasks: false,
599
+ quiet: false,
600
+ reportFormat: 'json'
601
+ };
602
+ // Check for commands
603
+ const commands = ['validate', 'schema', 'example', 'list', 'help', 'report', 'init', 'create'];
604
+ if (args.length > 0 && commands.includes(args[0])) {
605
+ command = args[0];
606
+ args = args.slice(1);
607
+ }
608
+ for (let i = 0; i < args.length; i++) {
609
+ const arg = args[i];
610
+ if (arg === '--help' || arg === '-h') {
611
+ options.help = true;
612
+ }
613
+ else if (arg === '--version' || arg === '-v') {
614
+ options.version = true;
615
+ }
616
+ else if (arg === '--schemas' || arg === '-s') {
617
+ options.schemasPath = args[++i];
618
+ }
619
+ else if (arg === '--type' || arg === '-t') {
620
+ const typeArg = args[++i];
621
+ if (['module', 'workflow', 'auto'].includes(typeArg)) {
622
+ options.type = typeArg;
623
+ }
624
+ else {
625
+ console.error(chalk_1.default.red(`Invalid type: ${typeArg}. Use: module, workflow, or auto`));
626
+ process.exit(2);
627
+ }
628
+ }
629
+ else if (arg === '--format' || arg === '-f') {
630
+ const formatArg = args[++i];
631
+ if (['pretty', 'json', 'compact'].includes(formatArg)) {
632
+ options.format = formatArg;
633
+ }
634
+ else {
635
+ console.error(chalk_1.default.red(`Invalid format: ${formatArg}. Use: pretty, json, or compact`));
636
+ process.exit(2);
637
+ }
638
+ }
639
+ else if (arg === '--verbose') {
640
+ options.verbose = true;
641
+ }
642
+ else if (arg === '--quiet' || arg === '-q') {
643
+ options.quiet = true;
644
+ }
645
+ else if (arg === '--json') {
646
+ options.format = 'json';
647
+ }
648
+ else if (arg === '--report' || arg === '-r') {
649
+ options.report = args[++i];
650
+ }
651
+ else if (arg === '--report-format') {
652
+ const reportFormatArg = args[++i];
653
+ if (['html', 'markdown', 'json'].includes(reportFormatArg)) {
654
+ options.reportFormat = reportFormatArg;
655
+ }
656
+ else {
657
+ console.error(chalk_1.default.red(`Invalid report format: ${reportFormatArg}. Use: html, markdown, or json`));
658
+ process.exit(2);
659
+ }
660
+ }
661
+ else if (!arg.startsWith('-')) {
662
+ files.push(arg);
663
+ }
664
+ else {
665
+ console.error(chalk_1.default.red(`Unknown option: ${arg}`));
666
+ console.error(`Use ${chalk_1.default.cyan(`${PROGRAM_NAME} --help`)} for usage information`);
667
+ process.exit(2);
668
+ }
669
+ }
670
+ // Handle schema command
671
+ if (command === 'schema' && files.length > 0) {
672
+ options.showSchema = files[0];
673
+ }
674
+ // Handle example command
675
+ if (command === 'example' && files.length > 0) {
676
+ options.showExample = files[0];
677
+ }
678
+ // Handle list command
679
+ if (command === 'list') {
680
+ options.listSchemas = true;
681
+ }
682
+ // Handle help command
683
+ if (command === 'help') {
684
+ options.help = true;
685
+ }
686
+ return { command, files, options };
687
+ }
688
+ // ============================================================================
689
+ // Schema Path Finding
690
+ // ============================================================================
691
+ function findSchemasPath() {
692
+ // Check environment variable
693
+ if (process.env.CX_SCHEMA_PATH && fs.existsSync(process.env.CX_SCHEMA_PATH)) {
694
+ return process.env.CX_SCHEMA_PATH;
695
+ }
696
+ // Check for .cx-schema in current directory
697
+ const localSchemas = path.join(process.cwd(), '.cx-schema');
698
+ if (fs.existsSync(localSchemas)) {
699
+ return localSchemas;
700
+ }
701
+ // Check for schemas in node_modules
702
+ const nodeModulesSchemas = path.join(process.cwd(), 'node_modules', '@cxtms/cx-schema', 'schemas');
703
+ if (fs.existsSync(nodeModulesSchemas)) {
704
+ return nodeModulesSchemas;
705
+ }
706
+ // Check in package directory (for development)
707
+ const packageSchemas = path.join(__dirname, '../schemas');
708
+ if (fs.existsSync(packageSchemas)) {
709
+ return packageSchemas;
710
+ }
711
+ return undefined;
712
+ }
713
+ // ============================================================================
714
+ // Auto-detection
715
+ // ============================================================================
716
+ function detectFileType(filePath) {
717
+ try {
718
+ const content = fs.readFileSync(filePath, 'utf-8');
719
+ const data = yaml.load(content);
720
+ if (data && typeof data === 'object') {
721
+ if ('workflow' in data) {
722
+ return 'workflow';
723
+ }
724
+ if ('module' in data || 'components' in data) {
725
+ return 'module';
726
+ }
727
+ }
728
+ // Check file path for hints
729
+ if (filePath.includes('workflow')) {
730
+ return 'workflow';
731
+ }
732
+ if (filePath.includes('module')) {
733
+ return 'module';
734
+ }
735
+ // Default to module
736
+ return 'module';
737
+ }
738
+ catch {
739
+ return 'module';
740
+ }
741
+ }
742
+ // ============================================================================
743
+ // Schema Display
744
+ // ============================================================================
745
+ function findSchemaFile(schemasPath, name, preferWorkflow = false) {
746
+ // Normalize name
747
+ const normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '');
748
+ // Workflow schema names - these should match workflow schemas first
749
+ const workflowCoreNames = ['workflow', 'activity', 'input', 'output', 'variable', 'trigger', 'schedule'];
750
+ const workflowTaskNames = [
751
+ 'foreach', 'switch', 'while', 'validation', 'map', 'setvariable', 'httprequest',
752
+ 'log', 'error', 'csv', 'export', 'template', 'graphql', 'order', 'contact',
753
+ 'commodity', 'job', 'attachment', 'email-send', 'document-render', 'document-send',
754
+ 'charge', 'accounting-transaction', 'payment', 'workflow-execute', 'condition',
755
+ 'expression', 'mapping'
756
+ ];
757
+ const isWorkflowSchema = workflowCoreNames.includes(normalizedName) ||
758
+ workflowTaskNames.includes(normalizedName);
759
+ // Search patterns in order of priority
760
+ const searchPaths = preferWorkflow || isWorkflowSchema
761
+ ? [
762
+ // Workflow schemas first for workflow-related names
763
+ path.join(schemasPath, 'workflows', `${name}.json`),
764
+ path.join(schemasPath, 'workflows', 'tasks', `${name}.json`),
765
+ path.join(schemasPath, 'workflows', 'common', `${name}.json`),
766
+ // Then module schemas
767
+ path.join(schemasPath, 'components', `${name}.json`),
768
+ path.join(schemasPath, 'fields', `${name}.json`),
769
+ path.join(schemasPath, 'actions', `${name}.json`)
770
+ ]
771
+ : [
772
+ // Module schemas first
773
+ path.join(schemasPath, 'components', `${name}.json`),
774
+ path.join(schemasPath, 'fields', `${name}.json`),
775
+ path.join(schemasPath, 'actions', `${name}.json`),
776
+ // Then workflow schemas
777
+ path.join(schemasPath, 'workflows', `${name}.json`),
778
+ path.join(schemasPath, 'workflows', 'tasks', `${name}.json`),
779
+ path.join(schemasPath, 'workflows', 'common', `${name}.json`)
780
+ ];
781
+ for (const schemaPath of searchPaths) {
782
+ if (fs.existsSync(schemaPath)) {
783
+ return schemaPath;
784
+ }
785
+ }
786
+ // Try fuzzy matching
787
+ const allSchemas = getAllSchemas(schemasPath);
788
+ for (const schema of allSchemas) {
789
+ const schemaBaseName = path.basename(schema, '.json').toLowerCase().replace(/[^a-z0-9]/g, '');
790
+ if (schemaBaseName === normalizedName || schemaBaseName.includes(normalizedName)) {
791
+ return schema;
792
+ }
793
+ }
794
+ return null;
795
+ }
796
+ function getAllSchemas(schemasPath) {
797
+ const schemas = [];
798
+ function scanDir(dir) {
799
+ if (!fs.existsSync(dir))
800
+ return;
801
+ const files = fs.readdirSync(dir);
802
+ for (const file of files) {
803
+ const filePath = path.join(dir, file);
804
+ const stat = fs.statSync(filePath);
805
+ if (stat.isDirectory()) {
806
+ scanDir(filePath);
807
+ }
808
+ else if (file.endsWith('.json')) {
809
+ schemas.push(filePath);
810
+ }
811
+ }
812
+ }
813
+ scanDir(schemasPath);
814
+ return schemas;
815
+ }
816
+ function showSchema(schemasPath, name) {
817
+ const schemaFile = findSchemaFile(schemasPath, name);
818
+ if (!schemaFile) {
819
+ console.error(chalk_1.default.red(`Schema not found: ${name}`));
820
+ console.error(chalk_1.default.gray(`Use '${PROGRAM_NAME} list' to see available schemas`));
821
+ process.exit(2);
822
+ }
823
+ const schema = JSON.parse(fs.readFileSync(schemaFile, 'utf-8'));
824
+ const relativePath = path.relative(schemasPath, schemaFile);
825
+ console.log(chalk_1.default.bold.cyan(`\nSchema: ${relativePath}\n`));
826
+ console.log(chalk_1.default.gray('─'.repeat(70)));
827
+ console.log(JSON.stringify(schema, null, 2));
828
+ console.log(chalk_1.default.gray('─'.repeat(70)));
829
+ }
830
+ function showExample(schemasPath, name) {
831
+ const schemaFile = findSchemaFile(schemasPath, name);
832
+ if (!schemaFile) {
833
+ console.error(chalk_1.default.red(`Schema not found: ${name}`));
834
+ console.error(chalk_1.default.gray(`Use '${PROGRAM_NAME} list' to see available schemas`));
835
+ process.exit(2);
836
+ }
837
+ const schema = JSON.parse(fs.readFileSync(schemaFile, 'utf-8'));
838
+ const relativePath = path.relative(schemasPath, schemaFile);
839
+ console.log(chalk_1.default.bold.cyan(`\nExample for: ${relativePath}\n`));
840
+ console.log(chalk_1.default.gray('─'.repeat(70)));
841
+ // Generate example from schema
842
+ const example = generateExampleFromSchema(schema, name);
843
+ console.log(yaml.dump(example, { indent: 2, lineWidth: 100 }));
844
+ console.log(chalk_1.default.gray('─'.repeat(70)));
845
+ }
846
+ function generateExampleFromSchema(schema, name) {
847
+ // Check for x-example or examples in schema
848
+ if (schema['x-example']) {
849
+ return schema['x-example'];
850
+ }
851
+ if (schema['x-examples'] && Array.isArray(schema['x-examples'])) {
852
+ return schema['x-examples'][0];
853
+ }
854
+ if (schema.examples && Array.isArray(schema.examples)) {
855
+ return schema.examples[0];
856
+ }
857
+ // Generate basic example from properties
858
+ const example = {};
859
+ if (schema.properties) {
860
+ for (const [key, prop] of Object.entries(schema.properties)) {
861
+ if (prop['x-example'] !== undefined) {
862
+ example[key] = prop['x-example'];
863
+ }
864
+ else if (prop.const !== undefined) {
865
+ example[key] = prop.const;
866
+ }
867
+ else if (prop.enum && prop.enum.length > 0) {
868
+ example[key] = prop.enum[0];
869
+ }
870
+ else if (prop.type === 'string') {
871
+ example[key] = prop.description ? `<${key}>` : 'example';
872
+ }
873
+ else if (prop.type === 'number' || prop.type === 'integer') {
874
+ example[key] = 1;
875
+ }
876
+ else if (prop.type === 'boolean') {
877
+ example[key] = true;
878
+ }
879
+ else if (prop.type === 'array') {
880
+ example[key] = [];
881
+ }
882
+ else if (prop.type === 'object') {
883
+ example[key] = {};
884
+ }
885
+ }
886
+ }
887
+ return example;
888
+ }
889
+ function listSchemas(schemasPath, type) {
890
+ console.log(chalk_1.default.bold.cyan('\n╔═══════════════════════════════════════════════════════════╗'));
891
+ console.log(chalk_1.default.bold.cyan('║ AVAILABLE SCHEMAS ║'));
892
+ console.log(chalk_1.default.bold.cyan('╚═══════════════════════════════════════════════════════════╝\n'));
893
+ if (type === 'auto' || type === 'module') {
894
+ console.log(chalk_1.default.bold.yellow('MODULE SCHEMAS:'));
895
+ console.log(chalk_1.default.gray('─'.repeat(50)));
896
+ // Components
897
+ const componentsDir = path.join(schemasPath, 'components');
898
+ if (fs.existsSync(componentsDir)) {
899
+ console.log(chalk_1.default.bold('\n Components:'));
900
+ const components = fs.readdirSync(componentsDir)
901
+ .filter(f => f.endsWith('.json'))
902
+ .map(f => f.replace('.json', ''));
903
+ console.log(chalk_1.default.green(' ' + components.join(', ')));
904
+ }
905
+ // Fields
906
+ const fieldsDir = path.join(schemasPath, 'fields');
907
+ if (fs.existsSync(fieldsDir)) {
908
+ console.log(chalk_1.default.bold('\n Fields:'));
909
+ const fields = fs.readdirSync(fieldsDir)
910
+ .filter(f => f.endsWith('.json'))
911
+ .map(f => f.replace('.json', ''));
912
+ console.log(chalk_1.default.green(' ' + fields.join(', ')));
913
+ }
914
+ // Actions
915
+ const actionsDir = path.join(schemasPath, 'actions');
916
+ if (fs.existsSync(actionsDir)) {
917
+ console.log(chalk_1.default.bold('\n Actions:'));
918
+ const actions = fs.readdirSync(actionsDir)
919
+ .filter(f => f.endsWith('.json'))
920
+ .map(f => f.replace('.json', ''));
921
+ console.log(chalk_1.default.green(' ' + actions.join(', ')));
922
+ }
923
+ }
924
+ if (type === 'auto' || type === 'workflow') {
925
+ console.log(chalk_1.default.bold.yellow('\nWORKFLOW SCHEMAS:'));
926
+ console.log(chalk_1.default.gray('─'.repeat(50)));
927
+ // Workflow core
928
+ const workflowsDir = path.join(schemasPath, 'workflows');
929
+ if (fs.existsSync(workflowsDir)) {
930
+ console.log(chalk_1.default.bold('\n Core:'));
931
+ const core = fs.readdirSync(workflowsDir)
932
+ .filter(f => f.endsWith('.json'))
933
+ .map(f => f.replace('.json', ''));
934
+ console.log(chalk_1.default.green(' ' + core.join(', ')));
935
+ // Tasks
936
+ const tasksDir = path.join(workflowsDir, 'tasks');
937
+ if (fs.existsSync(tasksDir)) {
938
+ console.log(chalk_1.default.bold('\n Tasks:'));
939
+ const tasks = fs.readdirSync(tasksDir)
940
+ .filter(f => f.endsWith('.json'))
941
+ .map(f => f.replace('.json', ''));
942
+ console.log(chalk_1.default.green(' ' + tasks.join(', ')));
943
+ }
944
+ // Common
945
+ const commonDir = path.join(workflowsDir, 'common');
946
+ if (fs.existsSync(commonDir)) {
947
+ console.log(chalk_1.default.bold('\n Common Definitions:'));
948
+ const common = fs.readdirSync(commonDir)
949
+ .filter(f => f.endsWith('.json'))
950
+ .map(f => f.replace('.json', ''));
951
+ console.log(chalk_1.default.green(' ' + common.join(', ')));
952
+ }
953
+ }
954
+ }
955
+ console.log(chalk_1.default.gray('\n─'.repeat(50)));
956
+ console.log(chalk_1.default.gray(`\nUse '${PROGRAM_NAME} schema <name>' to view a specific schema`));
957
+ console.log(chalk_1.default.gray(`Use '${PROGRAM_NAME} example <name>' to see an example\n`));
958
+ }
959
+ // ============================================================================
960
+ // Error Formatting
961
+ // ============================================================================
962
+ function getSchemaSnippet(schemasPath, error) {
963
+ if (!error.schemaPath)
964
+ return null;
965
+ // Try to extract component type from path
966
+ const pathMatch = error.path.match(/components?\[?\d*\]?\.?(\w+)?/);
967
+ if (pathMatch && pathMatch[1]) {
968
+ const schemaFile = findSchemaFile(schemasPath, pathMatch[1]);
969
+ if (schemaFile) {
970
+ try {
971
+ const schema = JSON.parse(fs.readFileSync(schemaFile, 'utf-8'));
972
+ return JSON.stringify(schema, null, 2).slice(0, 500) + '...';
973
+ }
974
+ catch {
975
+ return null;
976
+ }
977
+ }
978
+ }
979
+ return null;
980
+ }
981
+ function formatErrorPretty(error, index, schemasPath, verbose) {
982
+ const lines = [];
983
+ // Error header
984
+ lines.push(chalk_1.default.red(`\n┌─ Error #${index + 1}: ${error.type.toUpperCase().replace(/_/g, ' ')}`));
985
+ lines.push(chalk_1.default.red('│'));
986
+ // Path
987
+ lines.push(chalk_1.default.red('│ ') + chalk_1.default.bold('Path: ') + chalk_1.default.yellow(error.path || '/'));
988
+ // Message
989
+ lines.push(chalk_1.default.red('│ ') + chalk_1.default.bold('Message: ') + error.message);
990
+ // Schema path (verbose mode)
991
+ if (verbose && error.schemaPath) {
992
+ lines.push(chalk_1.default.red('│ ') + chalk_1.default.bold('Schema: ') + chalk_1.default.gray(error.schemaPath));
993
+ }
994
+ // Example (if available)
995
+ if (error.example !== undefined) {
996
+ lines.push(chalk_1.default.red('│'));
997
+ lines.push(chalk_1.default.red('│ ') + chalk_1.default.bold('Example:'));
998
+ const exampleLines = JSON.stringify(error.example, null, 2).split('\n');
999
+ exampleLines.forEach(line => {
1000
+ lines.push(chalk_1.default.red('│ ') + chalk_1.default.green(line));
1001
+ });
1002
+ }
1003
+ // Suggestion based on error type
1004
+ const suggestion = getSuggestion(error);
1005
+ if (suggestion) {
1006
+ lines.push(chalk_1.default.red('│'));
1007
+ lines.push(chalk_1.default.red('│ ') + chalk_1.default.bold('Suggestion: ') + chalk_1.default.cyan(suggestion));
1008
+ }
1009
+ lines.push(chalk_1.default.red('│'));
1010
+ lines.push(chalk_1.default.red('└' + '─'.repeat(60)));
1011
+ return lines.join('\n');
1012
+ }
1013
+ function getSuggestion(error) {
1014
+ switch (error.type) {
1015
+ case 'missing_property':
1016
+ const propMatch = error.message.match(/property:\s*(\w+)/i) || error.path.match(/\.(\w+)$/);
1017
+ if (propMatch) {
1018
+ return `Add the required property '${propMatch[1]}' to your YAML`;
1019
+ }
1020
+ return 'Check required properties in the schema';
1021
+ case 'schema_violation':
1022
+ if (error.message.includes('enum')) {
1023
+ return 'The value must be one of the allowed values. Check the schema for valid options.';
1024
+ }
1025
+ if (error.message.includes('type')) {
1026
+ return 'Check that the value type matches the expected type (string, number, boolean, etc.)';
1027
+ }
1028
+ if (error.message.includes('additionalProperties')) {
1029
+ return 'Remove unrecognized properties. Use `cx-validate schema <type>` to see allowed properties.';
1030
+ }
1031
+ return 'Review the schema requirements for this property';
1032
+ case 'yaml_syntax_error':
1033
+ return 'Check YAML indentation and syntax. Use a YAML linter to identify issues.';
1034
+ case 'invalid_task_type':
1035
+ return `Use 'cx-validate list --type workflow' to see available task types`;
1036
+ case 'invalid_activity':
1037
+ return 'Each activity must have a "name" and "steps" array';
1038
+ default:
1039
+ return null;
1040
+ }
1041
+ }
1042
+ function formatWarningPretty(warning, index) {
1043
+ const lines = [];
1044
+ lines.push(chalk_1.default.yellow(`\n⚠ Warning #${index + 1}: ${warning.type.toUpperCase().replace(/_/g, ' ')}`));
1045
+ lines.push(chalk_1.default.gray(` Path: ${warning.path}`));
1046
+ lines.push(` ${warning.message}`);
1047
+ return lines.join('\n');
1048
+ }
1049
+ // ============================================================================
1050
+ // Result Output
1051
+ // ============================================================================
1052
+ function printResultPretty(result, fileType, schemasPath, verbose) {
1053
+ const { summary, errors, warnings } = result;
1054
+ // Header
1055
+ console.log('\n' + chalk_1.default.bold.cyan('╔═══════════════════════════════════════════════════════════════════╗'));
1056
+ console.log(chalk_1.default.bold.cyan('║') + chalk_1.default.bold.white(' CX SCHEMA VALIDATION REPORT ') + chalk_1.default.bold.cyan('║'));
1057
+ console.log(chalk_1.default.bold.cyan('╚═══════════════════════════════════════════════════════════════════╝\n'));
1058
+ // Summary
1059
+ console.log(chalk_1.default.bold(' File: ') + summary.file);
1060
+ console.log(chalk_1.default.bold(' Type: ') + chalk_1.default.cyan(fileType === 'auto' ? 'auto-detected' : fileType));
1061
+ console.log(chalk_1.default.bold(' Time: ') + chalk_1.default.gray(summary.timestamp));
1062
+ console.log(chalk_1.default.bold(' Status: ') +
1063
+ (summary.status === 'PASSED'
1064
+ ? chalk_1.default.green.bold('✓ PASSED')
1065
+ : chalk_1.default.red.bold('✗ FAILED')));
1066
+ console.log(chalk_1.default.bold(' Errors: ') + (summary.errorCount > 0 ? chalk_1.default.red(summary.errorCount) : chalk_1.default.green('0')));
1067
+ console.log(chalk_1.default.bold(' Warnings:') + (summary.warningCount > 0 ? chalk_1.default.yellow(summary.warningCount) : chalk_1.default.green('0')));
1068
+ // Error breakdown
1069
+ if (summary.errorCount > 0) {
1070
+ console.log('\n' + chalk_1.default.bold(' Errors by Type:'));
1071
+ for (const [type, count] of Object.entries(summary.errorsByType)) {
1072
+ console.log(chalk_1.default.gray(` ${type}: `) + chalk_1.default.red(count));
1073
+ }
1074
+ }
1075
+ // Errors
1076
+ if (errors.length > 0) {
1077
+ console.log('\n' + chalk_1.default.bold.red('═══════════════════════════════════════════════════════════════════'));
1078
+ console.log(chalk_1.default.bold.red(' ERRORS'));
1079
+ console.log(chalk_1.default.bold.red('═══════════════════════════════════════════════════════════════════'));
1080
+ errors.forEach((error, index) => {
1081
+ console.log(formatErrorPretty(error, index, schemasPath, verbose));
1082
+ });
1083
+ }
1084
+ // Warnings
1085
+ if (warnings.length > 0) {
1086
+ console.log('\n' + chalk_1.default.bold.yellow('═══════════════════════════════════════════════════════════════════'));
1087
+ console.log(chalk_1.default.bold.yellow(' WARNINGS'));
1088
+ console.log(chalk_1.default.bold.yellow('═══════════════════════════════════════════════════════════════════'));
1089
+ warnings.forEach((warning, index) => {
1090
+ console.log(formatWarningPretty(warning, index));
1091
+ });
1092
+ }
1093
+ // Footer with help
1094
+ if (errors.length > 0) {
1095
+ console.log('\n' + chalk_1.default.gray('─'.repeat(70)));
1096
+ console.log(chalk_1.default.gray(' Tips:'));
1097
+ console.log(chalk_1.default.gray(` • Use '${PROGRAM_NAME} schema <name>' to view schema requirements`));
1098
+ console.log(chalk_1.default.gray(` • Use '${PROGRAM_NAME} example <name>' to see example YAML`));
1099
+ console.log(chalk_1.default.gray(` • Use '${PROGRAM_NAME} list' to see all available schemas`));
1100
+ console.log(chalk_1.default.gray('─'.repeat(70)));
1101
+ }
1102
+ console.log('');
1103
+ }
1104
+ function printResultCompact(result, filePath) {
1105
+ const status = result.isValid ? chalk_1.default.green('PASS') : chalk_1.default.red('FAIL');
1106
+ const errorInfo = result.summary.errorCount > 0 ? chalk_1.default.red(` (${result.summary.errorCount} errors)`) : '';
1107
+ console.log(`${status} ${filePath}${errorInfo}`);
1108
+ }
1109
+ function printResultJson(result) {
1110
+ console.log(JSON.stringify(result, null, 2));
1111
+ }
1112
+ // ============================================================================
1113
+ // Report Generation
1114
+ // ============================================================================
1115
+ function buildReportData(results) {
1116
+ const errorsByType = {};
1117
+ const errorsByFile = {};
1118
+ let totalErrors = 0;
1119
+ let totalWarnings = 0;
1120
+ for (const fileResult of results) {
1121
+ const errorCount = fileResult.result.errors.length;
1122
+ totalErrors += errorCount;
1123
+ totalWarnings += fileResult.result.warnings.length;
1124
+ if (errorCount > 0) {
1125
+ errorsByFile[fileResult.file] = errorCount;
1126
+ }
1127
+ for (const error of fileResult.result.errors) {
1128
+ const type = error.type || 'unknown';
1129
+ errorsByType[type] = (errorsByType[type] || 0) + 1;
1130
+ }
1131
+ }
1132
+ return {
1133
+ timestamp: new Date().toISOString(),
1134
+ totalFiles: results.length,
1135
+ passedFiles: results.filter(r => r.result.isValid).length,
1136
+ failedFiles: results.filter(r => !r.result.isValid).length,
1137
+ totalErrors,
1138
+ totalWarnings,
1139
+ errorsByType,
1140
+ errorsByFile,
1141
+ files: results
1142
+ };
1143
+ }
1144
+ function generateJsonReport(data) {
1145
+ return JSON.stringify(data, null, 2);
1146
+ }
1147
+ function generateMarkdownReport(data) {
1148
+ const lines = [];
1149
+ // Header
1150
+ lines.push('# CX Schema Validation Report');
1151
+ lines.push('');
1152
+ lines.push(`Generated: ${data.timestamp}`);
1153
+ lines.push('');
1154
+ // Summary
1155
+ lines.push('## Summary');
1156
+ lines.push('');
1157
+ lines.push('| Metric | Value |');
1158
+ lines.push('|--------|-------|');
1159
+ lines.push(`| Total Files | ${data.totalFiles} |`);
1160
+ lines.push(`| Passed | ${data.passedFiles} |`);
1161
+ lines.push(`| Failed | ${data.failedFiles} |`);
1162
+ lines.push(`| Total Errors | ${data.totalErrors} |`);
1163
+ lines.push(`| Total Warnings | ${data.totalWarnings} |`);
1164
+ lines.push(`| Pass Rate | ${((data.passedFiles / data.totalFiles) * 100).toFixed(1)}% |`);
1165
+ lines.push('');
1166
+ // Errors by Type
1167
+ if (Object.keys(data.errorsByType).length > 0) {
1168
+ lines.push('## Errors by Type');
1169
+ lines.push('');
1170
+ lines.push('| Error Type | Count |');
1171
+ lines.push('|------------|-------|');
1172
+ for (const [type, count] of Object.entries(data.errorsByType).sort((a, b) => b[1] - a[1])) {
1173
+ lines.push(`| ${type} | ${count} |`);
1174
+ }
1175
+ lines.push('');
1176
+ }
1177
+ // Failed Files
1178
+ const failedFiles = data.files.filter(f => !f.result.isValid);
1179
+ if (failedFiles.length > 0) {
1180
+ lines.push('## Failed Files');
1181
+ lines.push('');
1182
+ for (const fileResult of failedFiles) {
1183
+ lines.push(`### ${fileResult.file}`);
1184
+ lines.push('');
1185
+ lines.push(`- **Type:** ${fileResult.fileType}`);
1186
+ lines.push(`- **Errors:** ${fileResult.result.errors.length}`);
1187
+ lines.push(`- **Warnings:** ${fileResult.result.warnings.length}`);
1188
+ lines.push('');
1189
+ if (fileResult.result.errors.length > 0) {
1190
+ lines.push('**Errors:**');
1191
+ lines.push('');
1192
+ for (const error of fileResult.result.errors) {
1193
+ lines.push(`- **${error.type}** at \`${error.path || '/'}\`: ${error.message}`);
1194
+ }
1195
+ lines.push('');
1196
+ }
1197
+ }
1198
+ }
1199
+ // Passed Files (summary)
1200
+ const passedFiles = data.files.filter(f => f.result.isValid);
1201
+ if (passedFiles.length > 0) {
1202
+ lines.push('## Passed Files');
1203
+ lines.push('');
1204
+ for (const fileResult of passedFiles) {
1205
+ const warnings = fileResult.result.warnings.length;
1206
+ lines.push(`- ✓ ${fileResult.file}${warnings > 0 ? ` (${warnings} warnings)` : ''}`);
1207
+ }
1208
+ lines.push('');
1209
+ }
1210
+ return lines.join('\n');
1211
+ }
1212
+ function generateHtmlReport(data) {
1213
+ const passRate = ((data.passedFiles / data.totalFiles) * 100).toFixed(1);
1214
+ const passRateColor = data.failedFiles === 0 ? '#22c55e' : data.passedFiles > data.failedFiles ? '#eab308' : '#ef4444';
1215
+ const errorsByTypeRows = Object.entries(data.errorsByType)
1216
+ .sort((a, b) => b[1] - a[1])
1217
+ .map(([type, count]) => `<tr><td>${type}</td><td>${count}</td></tr>`)
1218
+ .join('\n');
1219
+ const failedFilesHtml = data.files
1220
+ .filter(f => !f.result.isValid)
1221
+ .map(f => {
1222
+ const errorsHtml = f.result.errors
1223
+ .map(e => `<li><strong>${e.type}</strong> at <code>${e.path || '/'}</code>: ${escapeHtml(e.message)}</li>`)
1224
+ .join('\n');
1225
+ return `
1226
+ <div class="file-card failed">
1227
+ <h3>✗ ${escapeHtml(f.file)}</h3>
1228
+ <p><strong>Type:</strong> ${f.fileType} | <strong>Errors:</strong> ${f.result.errors.length} | <strong>Warnings:</strong> ${f.result.warnings.length}</p>
1229
+ <ul class="error-list">${errorsHtml}</ul>
1230
+ </div>
1231
+ `;
1232
+ })
1233
+ .join('\n');
1234
+ const passedFilesHtml = data.files
1235
+ .filter(f => f.result.isValid)
1236
+ .map(f => {
1237
+ const warnings = f.result.warnings.length;
1238
+ return `<div class="file-card passed"><span>✓</span> ${escapeHtml(f.file)}${warnings > 0 ? ` <span class="warning-badge">${warnings} warnings</span>` : ''}</div>`;
1239
+ })
1240
+ .join('\n');
1241
+ return `<!DOCTYPE html>
1242
+ <html lang="en">
1243
+ <head>
1244
+ <meta charset="UTF-8">
1245
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1246
+ <title>CX Schema Validation Report</title>
1247
+ <style>
1248
+ * { box-sizing: border-box; margin: 0; padding: 0; }
1249
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; background: #f5f5f5; color: #333; line-height: 1.6; }
1250
+ .container { max-width: 1200px; margin: 0 auto; padding: 20px; }
1251
+ h1 { color: #1e40af; margin-bottom: 10px; }
1252
+ h2 { color: #374151; margin: 30px 0 15px; border-bottom: 2px solid #e5e7eb; padding-bottom: 10px; }
1253
+ h3 { color: #4b5563; margin-bottom: 10px; }
1254
+ .timestamp { color: #6b7280; margin-bottom: 30px; }
1255
+ .summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; margin-bottom: 30px; }
1256
+ .stat-card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); text-align: center; }
1257
+ .stat-card .value { font-size: 2rem; font-weight: bold; }
1258
+ .stat-card .label { color: #6b7280; font-size: 0.9rem; }
1259
+ .stat-card.passed .value { color: #22c55e; }
1260
+ .stat-card.failed .value { color: #ef4444; }
1261
+ .stat-card.rate .value { color: ${passRateColor}; }
1262
+ table { width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); margin-bottom: 20px; }
1263
+ th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #e5e7eb; }
1264
+ th { background: #f9fafb; font-weight: 600; }
1265
+ tr:last-child td { border-bottom: none; }
1266
+ .file-card { background: white; padding: 15px 20px; border-radius: 8px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
1267
+ .file-card.failed { border-left: 4px solid #ef4444; }
1268
+ .file-card.passed { border-left: 4px solid #22c55e; }
1269
+ .file-card.passed span { color: #22c55e; font-weight: bold; }
1270
+ .error-list { margin-top: 10px; padding-left: 20px; }
1271
+ .error-list li { margin-bottom: 8px; font-size: 0.9rem; }
1272
+ .error-list code { background: #f3f4f6; padding: 2px 6px; border-radius: 4px; font-size: 0.85rem; }
1273
+ .warning-badge { background: #fef3c7; color: #92400e; padding: 2px 8px; border-radius: 4px; font-size: 0.8rem; margin-left: 10px; }
1274
+ </style>
1275
+ </head>
1276
+ <body>
1277
+ <div class="container">
1278
+ <h1>CX Schema Validation Report</h1>
1279
+ <p class="timestamp">Generated: ${data.timestamp}</p>
1280
+
1281
+ <div class="summary-grid">
1282
+ <div class="stat-card">
1283
+ <div class="value">${data.totalFiles}</div>
1284
+ <div class="label">Total Files</div>
1285
+ </div>
1286
+ <div class="stat-card passed">
1287
+ <div class="value">${data.passedFiles}</div>
1288
+ <div class="label">Passed</div>
1289
+ </div>
1290
+ <div class="stat-card failed">
1291
+ <div class="value">${data.failedFiles}</div>
1292
+ <div class="label">Failed</div>
1293
+ </div>
1294
+ <div class="stat-card">
1295
+ <div class="value">${data.totalErrors}</div>
1296
+ <div class="label">Total Errors</div>
1297
+ </div>
1298
+ <div class="stat-card rate">
1299
+ <div class="value">${passRate}%</div>
1300
+ <div class="label">Pass Rate</div>
1301
+ </div>
1302
+ </div>
1303
+
1304
+ ${Object.keys(data.errorsByType).length > 0 ? `
1305
+ <h2>Errors by Type</h2>
1306
+ <table>
1307
+ <thead><tr><th>Error Type</th><th>Count</th></tr></thead>
1308
+ <tbody>${errorsByTypeRows}</tbody>
1309
+ </table>
1310
+ ` : ''}
1311
+
1312
+ ${data.failedFiles > 0 ? `
1313
+ <h2>Failed Files (${data.failedFiles})</h2>
1314
+ ${failedFilesHtml}
1315
+ ` : ''}
1316
+
1317
+ ${data.passedFiles > 0 ? `
1318
+ <h2>Passed Files (${data.passedFiles})</h2>
1319
+ ${passedFilesHtml}
1320
+ ` : ''}
1321
+ </div>
1322
+ </body>
1323
+ </html>`;
1324
+ }
1325
+ function escapeHtml(text) {
1326
+ return text
1327
+ .replace(/&/g, '&amp;')
1328
+ .replace(/</g, '&lt;')
1329
+ .replace(/>/g, '&gt;')
1330
+ .replace(/"/g, '&quot;')
1331
+ .replace(/'/g, '&#039;');
1332
+ }
1333
+ function detectReportFormat(filePath) {
1334
+ const ext = path.extname(filePath).toLowerCase();
1335
+ switch (ext) {
1336
+ case '.html':
1337
+ case '.htm':
1338
+ return 'html';
1339
+ case '.md':
1340
+ case '.markdown':
1341
+ return 'markdown';
1342
+ case '.json':
1343
+ default:
1344
+ return 'json';
1345
+ }
1346
+ }
1347
+ function generateReport(data, format) {
1348
+ switch (format) {
1349
+ case 'html':
1350
+ return generateHtmlReport(data);
1351
+ case 'markdown':
1352
+ return generateMarkdownReport(data);
1353
+ case 'json':
1354
+ default:
1355
+ return generateJsonReport(data);
1356
+ }
1357
+ }
1358
+ // ============================================================================
1359
+ // Validation
1360
+ // ============================================================================
1361
+ async function validateFile(filePath, options, schemasPath) {
1362
+ // Determine file type
1363
+ let fileType = options.type;
1364
+ if (fileType === 'auto') {
1365
+ fileType = detectFileType(filePath);
1366
+ }
1367
+ // Create appropriate validator
1368
+ if (fileType === 'workflow') {
1369
+ const validator = new workflowValidator_1.WorkflowValidator({
1370
+ schemasPath: path.join(schemasPath, 'workflows')
1371
+ });
1372
+ return validator.validateWorkflow(filePath);
1373
+ }
1374
+ else {
1375
+ const validator = new validator_1.ModuleValidator({ schemasPath });
1376
+ return validator.validateModule(filePath);
1377
+ }
1378
+ }
1379
+ // ============================================================================
1380
+ // Main
1381
+ // ============================================================================
1382
+ async function main() {
1383
+ const args = process.argv.slice(2);
1384
+ const { command, files, options } = parseArgs(args);
1385
+ // Handle help
1386
+ if (options.help) {
1387
+ if (command === 'schema') {
1388
+ console.log(SCHEMA_HELP);
1389
+ }
1390
+ else if (command === 'list') {
1391
+ console.log(LIST_HELP);
1392
+ }
1393
+ else if (command === 'init') {
1394
+ console.log(INIT_HELP);
1395
+ }
1396
+ else if (command === 'create') {
1397
+ console.log(CREATE_HELP);
1398
+ }
1399
+ else {
1400
+ console.log(HELP_TEXT);
1401
+ }
1402
+ process.exit(0);
1403
+ }
1404
+ // Handle version
1405
+ if (options.version) {
1406
+ console.log(`cx-validate v${VERSION}`);
1407
+ process.exit(0);
1408
+ }
1409
+ // Find schemas path
1410
+ const schemasPath = options.schemasPath || findSchemasPath();
1411
+ if (!schemasPath) {
1412
+ console.error(chalk_1.default.red('Error: Could not find schemas directory.'));
1413
+ console.error(chalk_1.default.gray('Please run npm install first or specify --schemas <path>'));
1414
+ process.exit(2);
1415
+ }
1416
+ // Handle schema command
1417
+ if (options.showSchema) {
1418
+ showSchema(schemasPath, options.showSchema);
1419
+ process.exit(0);
1420
+ }
1421
+ // Handle example command
1422
+ if (options.showExample) {
1423
+ showExample(schemasPath, options.showExample);
1424
+ process.exit(0);
1425
+ }
1426
+ // Handle list command
1427
+ if (options.listSchemas) {
1428
+ listSchemas(schemasPath, options.type);
1429
+ process.exit(0);
1430
+ }
1431
+ // Handle init command
1432
+ if (command === 'init') {
1433
+ runInit();
1434
+ process.exit(0);
1435
+ }
1436
+ // Handle create command
1437
+ if (command === 'create') {
1438
+ runCreate(files[0], files[1]);
1439
+ process.exit(0);
1440
+ }
1441
+ // Validate files
1442
+ if (files.length === 0) {
1443
+ console.error(chalk_1.default.red('Error: No input file specified'));
1444
+ console.error(chalk_1.default.gray(`Use '${PROGRAM_NAME} --help' for usage information`));
1445
+ process.exit(2);
1446
+ }
1447
+ let hasErrors = false;
1448
+ const allResults = [];
1449
+ const isReportMode = command === 'report' || options.report;
1450
+ for (const file of files) {
1451
+ // Check file exists
1452
+ if (!fs.existsSync(file)) {
1453
+ console.error(chalk_1.default.red(`Error: File not found: ${file}`));
1454
+ hasErrors = true;
1455
+ continue;
1456
+ }
1457
+ try {
1458
+ const fileType = options.type === 'auto' ? detectFileType(file) : options.type;
1459
+ const result = await validateFile(file, options, schemasPath);
1460
+ // Collect results for report
1461
+ if (isReportMode) {
1462
+ allResults.push({ file, fileType, result });
1463
+ }
1464
+ // Output individual results (unless quiet mode for reports)
1465
+ if (!isReportMode || !options.quiet) {
1466
+ if (options.format === 'json' && !isReportMode) {
1467
+ printResultJson(result);
1468
+ }
1469
+ else if (options.format === 'compact' || isReportMode) {
1470
+ printResultCompact(result, file);
1471
+ }
1472
+ else {
1473
+ printResultPretty(result, fileType, schemasPath, options.verbose);
1474
+ }
1475
+ }
1476
+ if (!result.isValid) {
1477
+ hasErrors = true;
1478
+ }
1479
+ }
1480
+ catch (error) {
1481
+ console.error(chalk_1.default.red(`Error validating ${file}:`), error.message);
1482
+ if (options.verbose) {
1483
+ console.error(chalk_1.default.gray(error.stack));
1484
+ }
1485
+ hasErrors = true;
1486
+ }
1487
+ }
1488
+ // Generate report if requested
1489
+ if (isReportMode && allResults.length > 0) {
1490
+ const reportData = buildReportData(allResults);
1491
+ const reportPath = options.report || 'validation-report.json';
1492
+ // Determine report format (auto-detect from extension if not specified explicitly)
1493
+ let reportFormat = options.reportFormat;
1494
+ if (!options.reportFormat || options.reportFormat === 'json') {
1495
+ // If reportFormat wasn't explicitly set, auto-detect from file extension
1496
+ const detectedFormat = detectReportFormat(reportPath);
1497
+ if (detectedFormat !== 'json' || !options.reportFormat) {
1498
+ reportFormat = detectedFormat;
1499
+ }
1500
+ }
1501
+ const reportContent = generateReport(reportData, reportFormat);
1502
+ fs.writeFileSync(reportPath, reportContent, 'utf-8');
1503
+ // Print summary
1504
+ console.log('');
1505
+ console.log(chalk_1.default.bold.cyan('═══════════════════════════════════════════════════════════════════'));
1506
+ console.log(chalk_1.default.bold.cyan(' VALIDATION SUMMARY'));
1507
+ console.log(chalk_1.default.bold.cyan('═══════════════════════════════════════════════════════════════════'));
1508
+ console.log('');
1509
+ console.log(chalk_1.default.bold(' Total Files: ') + reportData.totalFiles);
1510
+ console.log(chalk_1.default.bold(' Passed: ') + chalk_1.default.green(reportData.passedFiles));
1511
+ console.log(chalk_1.default.bold(' Failed: ') + (reportData.failedFiles > 0 ? chalk_1.default.red(reportData.failedFiles) : '0'));
1512
+ console.log(chalk_1.default.bold(' Pass Rate: ') + chalk_1.default.cyan(`${((reportData.passedFiles / reportData.totalFiles) * 100).toFixed(1)}%`));
1513
+ console.log('');
1514
+ console.log(chalk_1.default.gray(` Report saved to: ${chalk_1.default.white(reportPath)}`));
1515
+ console.log('');
1516
+ }
1517
+ process.exit(hasErrors ? 1 : 0);
1518
+ }
1519
+ main().catch(error => {
1520
+ console.error(chalk_1.default.red('Unexpected error:'), error.message);
1521
+ process.exit(2);
1522
+ });
1523
+ //# sourceMappingURL=cli.js.map