expressir 2.1.30 → 2.1.31

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +98 -0
  3. data/.github/workflows/links.yml +100 -0
  4. data/.github/workflows/rake.yml +4 -0
  5. data/.github/workflows/release.yml +5 -0
  6. data/.github/workflows/validate_schemas.yml +1 -1
  7. data/.gitignore +3 -0
  8. data/.rubocop.yml +1 -1
  9. data/.rubocop_todo.yml +244 -39
  10. data/Gemfile +2 -1
  11. data/README.adoc +621 -54
  12. data/docs/Gemfile +12 -0
  13. data/docs/_config.yml +141 -0
  14. data/docs/_guides/changes/changes-format.adoc +778 -0
  15. data/docs/_guides/changes/importing-eengine.adoc +898 -0
  16. data/docs/_guides/changes/index.adoc +396 -0
  17. data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
  18. data/docs/_guides/changes/validating-changes.adoc +681 -0
  19. data/docs/_guides/cli/benchmark-performance.adoc +834 -0
  20. data/docs/_guides/cli/coverage-analysis.adoc +921 -0
  21. data/docs/_guides/cli/format-schemas.adoc +547 -0
  22. data/docs/_guides/cli/index.adoc +8 -0
  23. data/docs/_guides/cli/managing-changes.adoc +927 -0
  24. data/docs/_guides/cli/validate-ascii.adoc +645 -0
  25. data/docs/_guides/cli/validate-schemas.adoc +534 -0
  26. data/docs/_guides/index.adoc +165 -0
  27. data/docs/_guides/ler/creating-packages.adoc +664 -0
  28. data/docs/_guides/ler/index.adoc +305 -0
  29. data/docs/_guides/ler/loading-packages.adoc +707 -0
  30. data/docs/_guides/ler/package-formats.adoc +748 -0
  31. data/docs/_guides/ler/querying-packages.adoc +826 -0
  32. data/docs/_guides/ler/validating-packages.adoc +750 -0
  33. data/docs/_guides/liquid/basic-templates.adoc +813 -0
  34. data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
  35. data/docs/_guides/liquid/drops-reference.adoc +829 -0
  36. data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
  37. data/docs/_guides/liquid/index.adoc +468 -0
  38. data/docs/_guides/manifests/creating-manifests.adoc +483 -0
  39. data/docs/_guides/manifests/index.adoc +307 -0
  40. data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
  41. data/docs/_guides/manifests/validating-manifests.adoc +713 -0
  42. data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
  43. data/docs/_guides/ruby-api/index.adoc +257 -0
  44. data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
  45. data/docs/_guides/ruby-api/search-engine.adoc +609 -0
  46. data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
  47. data/docs/_pages/data-model.adoc +665 -0
  48. data/docs/_pages/express-language.adoc +506 -0
  49. data/docs/_pages/getting-started.adoc +414 -0
  50. data/docs/_pages/index.adoc +116 -0
  51. data/docs/_pages/introduction.adoc +256 -0
  52. data/docs/_pages/ler-packages.adoc +837 -0
  53. data/docs/_pages/parsers.adoc +683 -0
  54. data/docs/_pages/schema-manifests.adoc +431 -0
  55. data/docs/_references/index.adoc +228 -0
  56. data/docs/_tutorials/creating-ler-package.adoc +735 -0
  57. data/docs/_tutorials/documentation-coverage.adoc +795 -0
  58. data/docs/_tutorials/index.adoc +221 -0
  59. data/docs/_tutorials/liquid-templates.adoc +806 -0
  60. data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
  61. data/docs/_tutorials/querying-schemas.adoc +751 -0
  62. data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
  63. data/docs/index.adoc +242 -0
  64. data/docs/lychee.toml +84 -0
  65. data/examples/demo_ler_usage.sh +86 -0
  66. data/examples/ler/README.md +111 -0
  67. data/examples/ler/simple_example.ler +0 -0
  68. data/examples/ler/simple_schema.exp +33 -0
  69. data/examples/ler_build.rb +75 -0
  70. data/examples/ler_cli.rb +79 -0
  71. data/examples/ler_demo_complete.rb +276 -0
  72. data/examples/ler_query.rb +91 -0
  73. data/examples/ler_query_examples.rb +305 -0
  74. data/examples/ler_stats.rb +81 -0
  75. data/examples/phase3_demo.rb +159 -0
  76. data/examples/query_demo_simple.rb +131 -0
  77. data/expressir.gemspec +2 -0
  78. data/lib/expressir/cli.rb +12 -4
  79. data/lib/expressir/commands/manifest.rb +427 -0
  80. data/lib/expressir/commands/package.rb +1274 -0
  81. data/lib/expressir/commands/validate.rb +70 -37
  82. data/lib/expressir/commands/validate_ascii.rb +607 -0
  83. data/lib/expressir/commands/validate_load.rb +88 -0
  84. data/lib/expressir/express/formatter.rb +5 -1
  85. data/lib/expressir/express/formatters/remark_item_formatter.rb +25 -0
  86. data/lib/expressir/express/parser.rb +33 -0
  87. data/lib/expressir/manifest/resolver.rb +213 -0
  88. data/lib/expressir/manifest/validator.rb +195 -0
  89. data/lib/expressir/model/declarations/entity.rb +6 -0
  90. data/lib/expressir/model/dependency_resolver.rb +270 -0
  91. data/lib/expressir/model/indexes/entity_index.rb +103 -0
  92. data/lib/expressir/model/indexes/reference_index.rb +148 -0
  93. data/lib/expressir/model/indexes/type_index.rb +149 -0
  94. data/lib/expressir/model/interface_validator.rb +384 -0
  95. data/lib/expressir/model/repository.rb +400 -5
  96. data/lib/expressir/model/repository_validator.rb +295 -0
  97. data/lib/expressir/model/search_engine.rb +525 -0
  98. data/lib/expressir/model.rb +4 -94
  99. data/lib/expressir/package/builder.rb +200 -0
  100. data/lib/expressir/package/metadata.rb +81 -0
  101. data/lib/expressir/package/reader.rb +165 -0
  102. data/lib/expressir/schema_manifest.rb +11 -1
  103. data/lib/expressir/version.rb +1 -1
  104. data/lib/expressir.rb +15 -2
  105. metadata +114 -4
  106. data/docs/benchmarking.adoc +0 -107
  107. data/docs/liquid_drops.adoc +0 -1547
data/lib/expressir/cli.rb CHANGED
@@ -7,9 +7,13 @@ require_relative "commands/clean"
7
7
  require_relative "commands/benchmark"
8
8
  require_relative "commands/benchmark_cache"
9
9
  require_relative "commands/validate"
10
+ require_relative "commands/validate_load"
11
+ require_relative "commands/validate_ascii"
10
12
  require_relative "commands/coverage"
11
13
  require_relative "commands/changes"
12
14
  require_relative "commands/version"
15
+ require_relative "commands/package"
16
+ require_relative "commands/manifest"
13
17
 
14
18
  module Expressir
15
19
  class Cli < Thor
@@ -55,10 +59,8 @@ module Expressir
55
59
  Commands::BenchmarkCache.new(options).run(path)
56
60
  end
57
61
 
58
- desc "validate *PATH", "validate EXPRESS schema located at PATH"
59
- def validate(*paths)
60
- Commands::Validate.new(options).run(paths)
61
- end
62
+ desc "validate SUBCOMMAND", "EXPRESS schema validation commands"
63
+ subcommand "validate", Commands::Validate
62
64
 
63
65
  desc "coverage *PATH",
64
66
  "List EXPRESS entities and check documentation coverage"
@@ -77,6 +79,12 @@ module Expressir
77
79
  desc "changes SUBCOMMAND", "Commands for EXPRESS Changes files"
78
80
  subcommand "changes", Commands::Changes
79
81
 
82
+ desc "manifest SUBCOMMAND", "Schema manifest management commands"
83
+ subcommand "manifest", Commands::Manifest
84
+
85
+ desc "package SUBCOMMAND", "LER package management commands"
86
+ subcommand "package", Commands::Package
87
+
80
88
  desc "version", "Expressir Version"
81
89
  def version
82
90
  Commands::Version.new(options).run
@@ -0,0 +1,427 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require_relative "../schema_manifest"
5
+ require_relative "../model/dependency_resolver"
6
+
7
+ module Expressir
8
+ module Commands
9
+ # Manifest management CLI commands
10
+ # Handles schema manifest creation and validation
11
+ class Manifest < Thor
12
+ include Thor::Actions
13
+
14
+ desc "create ROOT_SCHEMA [MORE_SCHEMAS...]",
15
+ "Generate schema manifest from root schema(s)"
16
+ long_desc <<~DESC
17
+ Generate a YAML manifest of all schemas required for packaging.
18
+
19
+ The manifest uses the existing SchemaManifest format:
20
+ schemas:
21
+ schema_id:
22
+ path: /path/to/schema.exp
23
+
24
+ Workflow:
25
+ 1. Create manifest from root schema
26
+ 2. Edit manifest to add paths for schemas with null paths
27
+ 3. Validate manifest
28
+ 4. Build package using manifest
29
+
30
+ Example:
31
+ expressir manifest create schemas/activity/mim.exp \\
32
+ -o activity_manifest.yaml \\
33
+ --base-dirs /path/to/schemas
34
+
35
+ # With multiple base directories (space-separated)
36
+ expressir manifest create schemas/activity/mim.exp \\
37
+ -o activity_manifest.yaml \\
38
+ --base-dirs /path/to/resources /path/to/modules
39
+
40
+ # Or comma-separated for backward compatibility
41
+ expressir manifest create schemas/activity/mim.exp \\
42
+ -o activity_manifest.yaml \\
43
+ --base-dirs /path/to/resources,/path/to/modules
44
+ DESC
45
+ option :output, aliases: "-o", type: :string, required: true,
46
+ desc: "Output YAML file path"
47
+ option :base_dirs, type: :array,
48
+ desc: "Base directories for schema resolution (can be specified multiple times)"
49
+ option :verbose, type: :boolean, default: false,
50
+ desc: "Show detailed output"
51
+ def create(*root_schemas)
52
+ # Convert string keys to symbols for compatibility with tests
53
+ opts = options.transform_keys(&:to_sym)
54
+
55
+ if root_schemas.empty?
56
+ say "Error: At least one root schema is required", :red
57
+ say "Usage: expressir manifest create ROOT_SCHEMA [MORE_SCHEMAS...] -o OUTPUT.yaml",
58
+ :yellow
59
+ raise Thor::Error, "At least one root schema is required"
60
+ end
61
+
62
+ # Verify root schemas exist
63
+ root_schemas.each do |schema|
64
+ unless File.exist?(schema)
65
+ say "Error: Root schema not found: #{schema}", :red
66
+ raise Thor::Error, "Root schema not found: #{schema}"
67
+ end
68
+ end
69
+
70
+ say "Creating manifest from #{root_schemas.size} root schema(s)..." if opts[:verbose]
71
+
72
+ # Parse base directories - now an array directly from Thor
73
+ base_dirs = if opts[:base_dirs]
74
+ # Thor array type accepts space-separated values in a single flag
75
+ # or we can also split comma-separated values for backward compatibility
76
+ opts[:base_dirs].flat_map do |dir|
77
+ dir.split(",")
78
+ end.map(&:strip)
79
+ else
80
+ # Default to directories containing root schemas
81
+ root_schemas.map do |s|
82
+ File.dirname(File.expand_path(s))
83
+ end.uniq
84
+ end
85
+
86
+ if opts[:verbose]
87
+ say " Base directories:"
88
+ base_dirs.each_with_index do |dir, index|
89
+ say " - [source #{index + 1}]: #{dir}"
90
+ end
91
+ end
92
+
93
+ # Resolve dependencies
94
+ say "Resolving dependencies..." if opts[:verbose]
95
+ resolver = Expressir::Model::DependencyResolver.new(
96
+ base_dirs: base_dirs,
97
+ verbose: opts[:verbose],
98
+ )
99
+
100
+ resolved_paths = resolver.resolve_dependencies(root_schemas.first)
101
+
102
+ # Create manifest using existing SchemaManifest class
103
+ manifest = Expressir::SchemaManifest.new
104
+
105
+ # Add resolved schemas - use actual schema name from file, not filename
106
+ resolved_paths.each do |path|
107
+ schema_id = extract_schema_name(path)
108
+ manifest.schemas << Expressir::SchemaManifestEntry.new(
109
+ id: schema_id,
110
+ path: path,
111
+ )
112
+ end
113
+
114
+ # Add unresolved schemas with empty string paths (not nil to avoid serialization issues)
115
+ resolver.unresolved.uniq { |e| e[:schema_name] }.each do |entry|
116
+ manifest.schemas << Expressir::SchemaManifestEntry.new(
117
+ id: entry[:schema_name],
118
+ path: "",
119
+ )
120
+ end
121
+
122
+ # Save manifest
123
+ FileUtils.mkdir_p(File.dirname(opts[:output]))
124
+ File.write(opts[:output], manifest.to_yaml)
125
+
126
+ # Use validator for consistent warnings (DRY - single source of truth)
127
+ require_relative "../manifest/validator"
128
+ validator = Expressir::Manifest::Validator.new(manifest, opts)
129
+ path_warnings = validator.validate_path_completeness
130
+
131
+ unresolved_count = path_warnings.size
132
+ resolved_count = manifest.schemas.size - unresolved_count
133
+
134
+ say "✓ Manifest created: #{opts[:output]}", :green
135
+ say " Resolved schemas: #{resolved_count}", :green
136
+
137
+ # Warn about multiple matches
138
+ stats = resolver.statistics
139
+ if stats[:multiple_matches].any?
140
+ say ""
141
+ say "⚠ Multiple matches found (#{stats[:multiple_matches].size}):",
142
+ :yellow
143
+ stats[:multiple_matches].each do |match_info|
144
+ say " Schema '#{match_info[:schema_name]}' found in #{match_info[:matches].size} locations:",
145
+ :yellow
146
+ match_info[:matches].each_with_index do |m, idx|
147
+ relative_path = m[:path].sub("#{m[:base_dir]}/", "")
148
+ marker = idx.zero? ? "[USING]" : " "
149
+ say " #{marker} [source #{m[:index] + 1}] #{relative_path}",
150
+ :yellow
151
+ end
152
+ end
153
+ say ""
154
+ say "The first match was used for each schema.", :yellow
155
+ say "If you need a different version, manually edit #{opts[:output]}",
156
+ :yellow
157
+ end
158
+
159
+ # Warn about unresolved using validator results
160
+ if path_warnings.any?
161
+ say ""
162
+ say "⚠ Unresolved schemas (#{unresolved_count}):", :yellow
163
+ path_warnings.each { |w| say " - #{w[:schema]}", :yellow }
164
+ say ""
165
+ say "Please edit #{opts[:output]} and set 'path:' for unresolved schemas",
166
+ :yellow
167
+ say "Then validate with: expressir manifest validate #{opts[:output]}",
168
+ :yellow
169
+ else
170
+ say " All schemas resolved successfully!", :green
171
+ end
172
+ rescue StandardError => e
173
+ say "Error creating manifest: #{e.message}", :red
174
+ say e.backtrace.join("\n") if options[:verbose]
175
+ raise Thor::Error, "Failed to create manifest: #{e.message}"
176
+ end
177
+
178
+ desc "resolve MANIFEST", "Resolve schema paths in manifest"
179
+ long_desc <<~DESC
180
+ Resolve missing or incomplete schema paths in a manifest file.
181
+
182
+ This command attempts to find file paths for schemas with missing or empty paths
183
+ by searching in base directories using naming patterns:
184
+
185
+ Supported patterns:
186
+ - Resource schemas: {schema_name}.exp
187
+ - Module ARM/MIM: {module_name}/{arm|mim}.exp
188
+ Example: Activity_method_mim -> activity_method/mim.exp
189
+
190
+ The resolved manifest is written to the output file, leaving the original unchanged.
191
+
192
+ Use this command after 'expressir manifest validate --check-references' fails
193
+ to automatically resolve missing schema paths.
194
+
195
+ Examples:
196
+ # Resolve paths using manifest's existing base directories
197
+ expressir manifest resolve manifest.yaml -o resolved_manifest.yaml
198
+
199
+ # Resolve with explicit base directories (space-separated)
200
+ expressir manifest resolve manifest.yaml -o resolved.yaml \\
201
+ --base-dirs /path/to/schemas /another/path
202
+
203
+ # Or comma-separated for backward compatibility
204
+ expressir manifest resolve manifest.yaml -o resolved.yaml \\
205
+ --base-dirs /path/to/schemas,/another/path
206
+
207
+ # With verbose output
208
+ expressir manifest resolve manifest.yaml -o resolved.yaml --verbose
209
+ DESC
210
+ option :output, aliases: "-o", type: :string, required: true,
211
+ desc: "Output file path for resolved manifest"
212
+ option :base_dirs, type: :array,
213
+ desc: "Base directories for schema search (can be specified multiple times)"
214
+ option :verbose, type: :boolean, default: false,
215
+ desc: "Show detailed resolution progress"
216
+ def resolve(manifest_path)
217
+ unless File.exist?(manifest_path)
218
+ say "Error: Manifest file not found: #{manifest_path}", :red
219
+ raise Thor::Error, "Manifest file not found: #{manifest_path}"
220
+ end
221
+
222
+ say "Resolving schema paths in: #{manifest_path}..." if options[:verbose]
223
+
224
+ # Load manifest
225
+ manifest = Expressir::SchemaManifest.from_file(manifest_path)
226
+
227
+ # Parse base directories - now an array directly from Thor
228
+ resolver_options = { verbose: options[:verbose] }
229
+ if options[:base_dirs]
230
+ # Thor array type accepts space-separated values in a single flag
231
+ # or we can also split comma-separated values for backward compatibility
232
+ resolver_options[:base_dirs] = options[:base_dirs].flat_map do |dir|
233
+ dir.split(",")
234
+ end.map(&:strip)
235
+ if options[:verbose]
236
+ say " Using base directories:"
237
+ resolver_options[:base_dirs].each_with_index do |dir, index|
238
+ say " - [source #{index + 1}]: #{dir}"
239
+ end
240
+ end
241
+ end
242
+
243
+ # Create resolver and resolve paths
244
+ require_relative "../manifest/resolver"
245
+ resolver = Expressir::Manifest::Resolver.new(manifest, resolver_options)
246
+
247
+ say "Attempting to resolve paths..." if options[:verbose]
248
+ resolved_manifest = resolver.resolve_paths
249
+
250
+ # Save resolved manifest
251
+ FileUtils.mkdir_p(File.dirname(options[:output]))
252
+ File.write(options[:output], resolved_manifest.to_yaml)
253
+
254
+ # Display statistics based on RESOLVED manifest (like manifest create)
255
+ require_relative "../manifest/validator"
256
+ validator = Expressir::Manifest::Validator.new(resolved_manifest,
257
+ options)
258
+ path_warnings = validator.validate_path_completeness
259
+
260
+ unresolved_count = path_warnings.size
261
+ resolved_count = resolved_manifest.schemas.size - unresolved_count
262
+
263
+ say "✓ Manifest resolved: #{options[:output]}", :green
264
+ say " Total schemas: #{resolved_manifest.schemas.size}", :green
265
+ say " Resolved schemas: #{resolved_count}", :green
266
+
267
+ # Warn about unresolved (like manifest create)
268
+ if path_warnings.any?
269
+ say ""
270
+ say "⚠ Unresolved schemas (#{unresolved_count}):", :yellow
271
+ path_warnings.each { |w| say " - #{w[:schema]}", :yellow }
272
+ say ""
273
+ say "These schemas could not be found in the search directories.",
274
+ :yellow
275
+ say "You may need to:", :yellow
276
+ say " 1. Add more base directories with --base-dirs", :yellow
277
+ say " 2. Manually edit #{options[:output]} and set their paths",
278
+ :yellow
279
+ else
280
+ say " All schema paths resolved successfully!", :green
281
+ end
282
+ rescue StandardError => e
283
+ say "Error resolving manifest: #{e.message}", :red
284
+ say e.backtrace.join("\n") if options[:verbose]
285
+ raise Thor::Error, "Failed to resolve manifest: #{e.message}"
286
+ end
287
+
288
+ desc "validate MANIFEST", "Validate a schema manifest"
289
+ long_desc <<~DESC
290
+ Validate a schema manifest file.
291
+
292
+ Validation types:
293
+ - File existence: All schema paths exist on disk
294
+ - Path completeness: All schemas have paths specified (warnings)
295
+ - Referential integrity (--check-references): All USE/REFERENCE FROM resolve
296
+
297
+ Examples:
298
+ # Basic validation (file existence and path completeness)
299
+ expressir manifest validate activity_manifest.yaml
300
+
301
+ # With referential integrity checking
302
+ expressir manifest validate activity_manifest.yaml --check-references
303
+
304
+ # With verbose output
305
+ expressir manifest validate activity_manifest.yaml --check-references --verbose
306
+ DESC
307
+ option :verbose, type: :boolean, default: false,
308
+ desc: "Show detailed validation results"
309
+ option :check_references, type: :boolean, default: false,
310
+ desc: "Validate referential integrity using dependency resolver"
311
+ def validate(manifest_path)
312
+ unless File.exist?(manifest_path)
313
+ say "Error: Manifest file not found: #{manifest_path}", :red
314
+ raise Thor::Error, "Manifest file not found: #{manifest_path}"
315
+ end
316
+
317
+ # Convert string keys to symbols for compatibility with tests
318
+ opts = options.transform_keys(&:to_sym)
319
+
320
+ say "Validating manifest: #{manifest_path}..." if opts[:verbose]
321
+
322
+ # Load manifest
323
+ manifest = Expressir::SchemaManifest.from_file(manifest_path)
324
+
325
+ # Create validator - SINGLE SOURCE OF TRUTH
326
+ require_relative "../manifest/validator"
327
+ validator = Expressir::Manifest::Validator.new(manifest, opts)
328
+
329
+ # Run validations using validator (NO INLINE LOGIC)
330
+ file_errors = validator.validate_file_existence
331
+ path_warnings = validator.validate_path_completeness
332
+ name_errors = validator.validate_schema_names
333
+
334
+ reference_errors = []
335
+ if opts[:check_references]
336
+ say "Checking referential integrity..." if opts[:verbose]
337
+ reference_errors = validator.validate_referential_integrity
338
+ end
339
+
340
+ # Display results
341
+ display_validation_results(manifest, file_errors, path_warnings,
342
+ name_errors, reference_errors)
343
+ rescue StandardError => e
344
+ say "Error validating manifest: #{e.message}", :red
345
+ say e.backtrace.join("\n") if options && options[:verbose]
346
+ raise Thor::Error, "Failed to validate manifest: #{e.message}"
347
+ end
348
+
349
+ private
350
+
351
+ # Extract the actual schema name from a schema file by parsing it
352
+ # @param schema_path [String] Path to the schema file
353
+ # @return [String] The schema name declared in the file
354
+ def extract_schema_name(schema_path)
355
+ require_relative "../express/parser"
356
+ repo = Expressir::Express::Parser.from_file(schema_path)
357
+ schema = repo.schemas.first
358
+ schema&.id || File.basename(schema_path, ".*")
359
+ rescue StandardError
360
+ # Fallback to filename if parsing fails
361
+ File.basename(schema_path, ".*")
362
+ end
363
+
364
+ def display_validation_results(manifest, file_errors, path_warnings,
365
+ name_errors, reference_errors)
366
+ # Convert string keys to symbols for compatibility with tests
367
+ opts = options.transform_keys(&:to_sym)
368
+
369
+ # Calculate counts
370
+ unresolved_count = manifest.schemas.count do |s|
371
+ s.path.nil? || s.path.empty?
372
+ end
373
+ resolved_count = manifest.schemas.size - unresolved_count
374
+
375
+ # Determine if validation passed
376
+ has_errors = file_errors.any? || name_errors.any? || reference_errors.any?
377
+
378
+ if has_errors
379
+ say "✗ Manifest validation failed", :red
380
+ say ""
381
+
382
+ if file_errors.any?
383
+ say "File Errors (#{file_errors.size}):", :red
384
+ file_errors.each { |e| say " - #{e[:message]}", :red }
385
+ say ""
386
+ end
387
+
388
+ if name_errors.any?
389
+ say "Schema Name Errors (#{name_errors.size}):", :red
390
+ name_errors.each { |e| say " - #{e[:message]}", :red }
391
+ say ""
392
+ end
393
+
394
+ if reference_errors.any?
395
+ say "Reference Errors (#{reference_errors.size}):", :red
396
+ reference_errors.each { |e| say " - #{e[:message]}", :red }
397
+ say ""
398
+ end
399
+
400
+ if path_warnings.any?
401
+ say "Warnings (#{path_warnings.size}):", :yellow
402
+ path_warnings.each { |w| say " - #{w[:message]}", :yellow }
403
+ end
404
+
405
+ raise Thor::Error, "Manifest validation failed"
406
+ else
407
+ say "✓ Manifest is valid", :green
408
+ if opts[:verbose]
409
+ say " Total schemas: #{manifest.schemas.size}",
410
+ :green
411
+ end
412
+ say " Resolved schemas: #{resolved_count}", :green if opts[:verbose]
413
+ if opts[:verbose] && unresolved_count.positive?
414
+ say " Unresolved schemas: #{unresolved_count}",
415
+ :green
416
+ end
417
+
418
+ if path_warnings.any?
419
+ say ""
420
+ say "Warnings (#{path_warnings.size}):", :yellow
421
+ path_warnings.each { |w| say " - #{w[:message]}", :yellow }
422
+ end
423
+ end
424
+ end
425
+ end
426
+ end
427
+ end