suma 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +3 -0
  3. data/.github/workflows/release.yml +5 -1
  4. data/.gitignore +10 -1
  5. data/.rubocop_todo.yml +237 -28
  6. data/CLAUDE.md +102 -0
  7. data/Gemfile +3 -1
  8. data/README.adoc +188 -1
  9. data/exe/suma +1 -1
  10. data/lib/suma/cli/build.rb +2 -8
  11. data/lib/suma/cli/check_svg_quality.rb +172 -0
  12. data/lib/suma/cli/compare.rb +6 -158
  13. data/lib/suma/cli/convert_jsdai.rb +0 -2
  14. data/lib/suma/cli/core.rb +119 -0
  15. data/lib/suma/cli/export.rb +1 -10
  16. data/lib/suma/cli/extract_terms.rb +10 -654
  17. data/lib/suma/cli/generate_register.rb +34 -0
  18. data/lib/suma/cli/generate_schemas.rb +8 -124
  19. data/lib/suma/cli/reformat.rb +0 -1
  20. data/lib/suma/cli/validate.rb +0 -2
  21. data/lib/suma/cli/validate_links.rb +14 -291
  22. data/lib/suma/cli.rb +12 -102
  23. data/lib/suma/collection_config.rb +0 -2
  24. data/lib/suma/collection_manifest.rb +7 -111
  25. data/lib/suma/eengine/wrapper.rb +0 -1
  26. data/lib/suma/eengine.rb +8 -0
  27. data/lib/suma/express_schema.rb +43 -31
  28. data/lib/suma/jsdai/figure.rb +0 -3
  29. data/lib/suma/jsdai/figure_xml.rb +12 -9
  30. data/lib/suma/jsdai.rb +5 -8
  31. data/lib/suma/link_validator.rb +211 -0
  32. data/lib/suma/manifest_traverser.rb +92 -0
  33. data/lib/suma/processor.rb +76 -105
  34. data/lib/suma/register_manifest_generator.rb +163 -0
  35. data/lib/suma/schema_category.rb +83 -0
  36. data/lib/suma/schema_collection.rb +28 -63
  37. data/lib/suma/schema_comparer.rb +117 -0
  38. data/lib/suma/schema_compiler.rb +86 -0
  39. data/lib/suma/schema_discovery.rb +75 -0
  40. data/lib/suma/schema_exporter.rb +7 -35
  41. data/lib/suma/schema_index.rb +53 -0
  42. data/lib/suma/schema_manifest_generator.rb +113 -0
  43. data/lib/suma/schema_naming.rb +111 -0
  44. data/lib/suma/schema_template/document.rb +141 -0
  45. data/lib/suma/schema_template/plain.rb +46 -0
  46. data/lib/suma/schema_template.rb +19 -0
  47. data/lib/suma/svg_quality/batch_report.rb +78 -0
  48. data/lib/suma/svg_quality/formatters/json_formatter.rb +30 -0
  49. data/lib/suma/svg_quality/formatters/terminal_formatter.rb +168 -0
  50. data/lib/suma/svg_quality/formatters/yaml_formatter.rb +32 -0
  51. data/lib/suma/svg_quality/formatters.rb +12 -0
  52. data/lib/suma/svg_quality/report.rb +52 -0
  53. data/lib/suma/svg_quality.rb +30 -0
  54. data/lib/suma/term_extractor.rb +466 -0
  55. data/lib/suma/urn.rb +61 -0
  56. data/lib/suma/utils.rb +10 -2
  57. data/lib/suma/version.rb +1 -1
  58. data/lib/suma.rb +34 -5
  59. data/suma.gemspec +3 -2
  60. metadata +53 -9
  61. data/lib/suma/export_standalone_schema.rb +0 -14
  62. data/lib/suma/schema_attachment.rb +0 -130
  63. data/lib/suma/schema_document.rb +0 -132
data/README.adoc CHANGED
@@ -39,7 +39,8 @@ $ suma
39
39
  Commands:
40
40
  suma build METANORMA_SITE_MANIFEST # Build collection specified in site manifest (`metanorma*.yml`)
41
41
  suma convert-jsdai XML_FILE IMAGE_FILE OUTPUT_DIR # Convert JSDAI XML and image files to SVG and EXP files
42
- suma extract-terms SCHEMA_MANIFEST_FILE GLOSSARIST_OUTPUT_PATH # Extract terms from SCHEMA_MANIFEST_FILE into Glossarist v2 format
42
+ suma extract-terms SCHEMA_MANIFEST_FILE GLOSSARIST_OUTPUT_PATH # Extract EXPRESS entity concepts into Glossarist v3 format
43
+ suma generate-register SCHEMA_MANIFEST_FILE OUTPUT_PATH # Generate hierarchical register.yaml from schema manifest
43
44
  suma generate-schemas METANORMA_MANIFEST_FILE SCHEMA_MANIFEST_FILE # Generate EXPRESS schema manifest file from Metanorma site manifest
44
45
  suma help [COMMAND] # Describe available commands or one specific command
45
46
  suma reformat EXPRESS_FILE_PATH # Reformat EXPRESS files
@@ -301,6 +302,61 @@ $ bundle exec suma extract-terms schemas-activity-modules.yml terms_output
301
302
  ====
302
303
 
303
304
 
305
+ === Generate register command
306
+
307
+ The `suma generate-register` command reads an EXPRESS schema manifest and
308
+ emits a Glossarist v3 `register.yaml` with hierarchical sections. The
309
+ output is used by the concept browser to render a navigable sidebar of
310
+ sections and to resolve concept cross-references.
311
+
312
+ [source,sh]
313
+ ----
314
+ $ suma generate-register SCHEMA_MANIFEST_FILE OUTPUT_PATH -u URN --id ID --ref REF
315
+ ----
316
+
317
+ Parameters:
318
+
319
+ `SCHEMA_MANIFEST_FILE`:: Path to the schema manifest (e.g., "schemas-smrl-part-2.yml")
320
+
321
+ `OUTPUT_PATH`:: Directory where `register.yaml` will be written
322
+
323
+ Options:
324
+
325
+ `--urn`, `-u`:: Base URN prefix for the dataset (e.g. `urn:iso:std:iso:10303:-2:ed-2:en:tech:*`).
326
+ The wildcard is stripped for the base URN and kept in `urnAliases`.
327
+ *Required*.
328
+
329
+ The URN is fully configurable — every component can be changed to match the
330
+ dataset being exported. The general structure is:
331
+
332
+ ----
333
+ urn:iso:std:iso:<part>:ed-<edition>:<lang>:tech:*
334
+ ----
335
+
336
+ For example, to export edition 3 in French:
337
+
338
+ ----
339
+ --urn urn:iso:std:iso:10303:-2:ed-3:fr:tech:*
340
+ ----
341
+
342
+ The current ISO 10303-2 SMRL data is edition 2; use `ed-2` unless you are
343
+ re-publishing an earlier edition.
344
+
345
+ `--id`:: Dataset identifier (e.g. `iso10303-2-express`). *Required*.
346
+
347
+ `--ref`:: Human-readable dataset reference label (e.g. `ISO 10303-2 EXPRESS Concepts`). *Required*.
348
+
349
+ `--language_code`, `-l`:: Language code for section names (default: "eng")
350
+
351
+ The generated register contains two top-level groups:
352
+
353
+ * **Resources** — integrated resource schemas (path contains `schemas/resources/`)
354
+ * **Application Modules** — ARM and MIM schemas (path contains `schemas/modules/`)
355
+
356
+ Each schema gets a human-readable name via the `SchemaNaming` module
357
+ (e.g. `topology_schema` → "Resource: Topology", `Activity_method_assignment_mim` → "Module: Activity Method Assignment (MIM)"). Acronyms (AIC, AEC, BREP, 2D, 3D) are preserved; function words (as, of, the) are lowercased per ISO title-case convention.
358
+
359
+
304
360
  === Convert JSDAI image outputs to SVG and Annotated EXPRESS
305
361
 
306
362
  The `suma convert-jsdai` command converts JSDAI EXPRESS-G diagram outputs
@@ -864,6 +920,137 @@ For complete documentation of all Expressir commands and options, see the
864
920
  https://github.com/lutaml/expressir[Expressir documentation].
865
921
 
866
922
 
923
+ === Check SVG quality command
924
+
925
+ ==== General
926
+
927
+ The `check_svg_quality` command validates SVG files for conformance and quality
928
+ using the https://github.com/claricle/svg_conform[svg_conform] library. It supports
929
+ both batch directory scanning and single file analysis.
930
+
931
+ By default, it uses the `metanorma` profile which allows raster images
932
+ (PNG, JPEG) and other features specific to Metanorma documentation.
933
+
934
+ [source,sh]
935
+ ----
936
+ $ suma check_svg_quality [PATH] [options]
937
+ ----
938
+
939
+ Parameters:
940
+
941
+ `PATH`:: Path to directory containing SVG files or a single SVG file.
942
+ Defaults to `schemas` directory.
943
+
944
+ ==== Batch directory scanning
945
+
946
+ When given a directory, it scans all SVG files and produces a quality report
947
+ sorted by error count (most problematic files first).
948
+
949
+ [example]
950
+ .Check all SVG files in schemas directory
951
+ [source,sh]
952
+ ----
953
+ $ suma check_svg_quality ../iso-10303/schemas
954
+ ----
955
+
956
+ .Output
957
+ ----
958
+ 🔍 Scanning 3878 SVG files...
959
+
960
+ ╔══════════════════════════════════════════════════════════════════════════════╗
961
+ ║ 🔍 SVG Quality Report Sorted by error count (most first) ║
962
+ ╚══════════════════════════════════════════════════════════════════════════════╝
963
+
964
+ 📊 OVERVIEW
965
+
966
+ ● Total Files : 3878
967
+ ● Valid : 3845 ✅
968
+ ● Invalid : 33 ❌
969
+ ...
970
+
971
+ 💥 CRITICAL QUALITY (2 files)
972
+
973
+ ✗ 0/100 813 errors schemas/resources/action_schema/action_schemaexpg2.svg
974
+ ✗ 25/100 156 errors schemas/modules/activity/mimexpg1.svg
975
+ ----
976
+
977
+ ==== Single file analysis
978
+
979
+ When given a path to a single SVG file, it shows detailed error breakdown
980
+ grouped by requirement type.
981
+
982
+ [example]
983
+ .Analyze a single SVG file
984
+ [source,sh]
985
+ ----
986
+ $ suma check_svg_quality schemas/resources/action_schema/action_schemaexpg2.svg
987
+ ----
988
+
989
+ .Output
990
+ ----
991
+ 📄 SVG Quality Report: schemas/resources/action_schema/action_schemaexpg2.svg
992
+
993
+ Valid: NO ❌
994
+ Errors: 813
995
+
996
+ 📋 Error Details
997
+
998
+ style (56 occurrences)
999
+ - Style property 'enable-background' removed
1000
+ - Style property 'overflow' removed
1001
+ ...
1002
+
1003
+ allowed_elements (109 occurrences)
1004
+ - The element 'image' is not allowed as a child of 'svg'
1005
+ ...
1006
+
1007
+ color_restrictions (108 occurrences)
1008
+ - Color 'rgb(33, 128, 255)' in style property 'fill' is not allowed
1009
+ ...
1010
+ ----
1011
+
1012
+ Options:
1013
+
1014
+ `--profile=PROFILE`:: Validation profile to use (default: `metanorma`).
1015
+ Other options: `svg_1_2_rfc`, `svg_1_2_rfc_with_rdf`, `base`, `lucid_fix`
1016
+
1017
+ `--sort=MODE`:: Sort order: `errors` (most errors first, default) or
1018
+ `quality` (lowest quality scores first)
1019
+
1020
+ `--format=FORMAT`:: Output format: `terminal` (default), `json`, or `yaml`
1021
+
1022
+ `--output=PATH`, `-o PATH`:: Write output to file instead of stdout
1023
+
1024
+ `--min_errors=N`:: Filter to show only files with at least N errors
1025
+
1026
+ `--limit=N`:: Limit output to top N files
1027
+
1028
+ `--progress`:: Show progress during batch scanning
1029
+
1030
+ `--summary-only`:: Show only summary, not individual file details
1031
+
1032
+ [example]
1033
+ .Check with custom profile
1034
+ [source,sh]
1035
+ ----
1036
+ $ suma check_svg_quality schemas/ --profile svg_1_2_rfc
1037
+ ----
1038
+
1039
+ [example]
1040
+ .Output as JSON
1041
+ [source,sh]
1042
+ ----
1043
+ $ suma check_svg_quality schemas/ --format json --output quality_report.json
1044
+ ----
1045
+
1046
+ [example]
1047
+ .Show only files with 100+ errors
1048
+ [source,sh]
1049
+ ----
1050
+ $ suma check_svg_quality schemas/ --min_errors 100
1051
+ ----
1052
+
1053
+
867
1054
  == Usage: Ruby
868
1055
 
869
1056
  === General
data/exe/suma CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "suma/cli"
4
+ require "suma"
5
5
 
6
6
  Suma::Cli::Core.start(ARGV)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "thor"
4
- require_relative "../thor_ext"
5
4
 
6
5
  module Suma
7
6
  module Cli
@@ -15,10 +14,6 @@ module Suma
15
14
  desc: "Generate file that contains all schemas in the collection."
16
15
 
17
16
  def build(metanorma_site_manifest)
18
- # Lazy-load dependencies only when this command is actually used
19
- require_relative "../processor"
20
- require_relative "../utils"
21
-
22
17
  unless File.exist?(metanorma_site_manifest)
23
18
  raise Errno::ENOENT, "Specified Metanorma site manifest file " \
24
19
  "`#{metanorma_site_manifest}` not found."
@@ -31,16 +26,15 @@ module Suma
31
26
  private
32
27
 
33
28
  def run(manifest, options)
34
- # Set schemas_all_path to match metanorma_yaml_path
35
29
  schemas_all_path = options[:schemas_all_path] ||
36
30
  manifest.gsub("metanorma", "schemas")
37
31
 
38
- Processor.run(
32
+ Processor.new(
39
33
  metanorma_yaml_path: manifest,
40
34
  schemas_all_path: schemas_all_path,
41
35
  compile: options[:compile],
42
36
  output_directory: "_site",
43
- )
37
+ ).run
44
38
  end
45
39
 
46
40
  def log_error(error)
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module Suma
6
+ module Cli
7
+ # Check SVG quality using svg_conform Validator API - thin CLI wrapper
8
+ class CheckSvgQuality
9
+ DATA_PATH = "schemas"
10
+ DEFAULT_PATTERN = "**/*.svg"
11
+ DEFAULT_PROFILE = :metanorma
12
+
13
+ def initialize(pattern: DEFAULT_PATTERN, profile: DEFAULT_PROFILE,
14
+ format: "terminal", output: nil, min_errors: nil,
15
+ summary_only: false, progress: false, limit: nil,
16
+ sort: "errors")
17
+ @options = {
18
+ pattern: pattern,
19
+ profile: profile,
20
+ format: format,
21
+ output: output,
22
+ min_errors: min_errors,
23
+ summary_only: summary_only,
24
+ progress: progress,
25
+ limit: limit,
26
+ sort: sort.to_sym,
27
+ }
28
+ end
29
+
30
+ def run(path = DATA_PATH)
31
+ require "svg_conform"
32
+
33
+ path_obj = Pathname.new(path).expand_path
34
+
35
+ # Enable progress by default when outputting to terminal
36
+ show_progress = options[:progress] || ($stdout.tty? && !options[:output])
37
+ if show_progress
38
+ $stdout.sync = true
39
+ $stderr.sync = true
40
+ end
41
+
42
+ if path_obj.file?
43
+ # Single file mode - show detailed errors
44
+ analyze_single_file(path_obj)
45
+ else
46
+ # Directory mode - show batch report
47
+ svg_files = find_svg_files(path_obj)
48
+
49
+ if svg_files.empty?
50
+ puts "No SVG files found in #{path}"
51
+ return
52
+ end
53
+
54
+ puts "🔍 Scanning #{svg_files.size} SVG files..."
55
+ puts
56
+
57
+ reports = analyze_files_one_by_one(svg_files, show_progress)
58
+ batch_report = SvgQuality::BatchReport.new(reports)
59
+ sorted_report = sort_report(batch_report)
60
+ output_report(sorted_report)
61
+ end
62
+ end
63
+
64
+ def analyze_single_file(path)
65
+ validator = SvgConform::Validator.new
66
+ result = validator.validate_file(path.to_s, profile: options[:profile])
67
+
68
+ puts "📄 SVG Quality Report: #{path}"
69
+ puts ""
70
+ puts " Valid: #{result.valid? ? 'YES ✅' : 'NO ❌'}"
71
+ puts " Errors: #{result.error_count}"
72
+ puts ""
73
+
74
+ if result.errors.any?
75
+ puts " 📋 Error Details"
76
+ puts ""
77
+
78
+ # Group errors by requirement_id
79
+ by_req = result.errors.group_by(&:requirement_id)
80
+
81
+ by_req.each do |req_id, errors|
82
+ puts " #{req_id} (#{errors.size} occurrences)"
83
+ errors.first(5).each do |e|
84
+ puts " - #{e.message}"
85
+ end
86
+ if errors.size > 5
87
+ puts " ... and #{errors.size - 5} more"
88
+ end
89
+ puts ""
90
+ end
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ attr_reader :options
97
+
98
+ def find_svg_files(path)
99
+ if path.directory?
100
+ Pathname.glob(path.join(options[:pattern])).select(&:file?)
101
+ elsif path.file? && path.extname == ".svg"
102
+ [path]
103
+ else
104
+ []
105
+ end
106
+ end
107
+
108
+ def analyze_files_one_by_one(files, show_progress = false)
109
+ validator = SvgConform::Validator.new
110
+ reports = []
111
+
112
+ files.each_with_index do |file, index|
113
+ result = validator.validate_file(file.to_s,
114
+ profile: options[:profile])
115
+ report = SvgQuality::Report.new(file.to_s, result)
116
+ reports << report
117
+
118
+ if show_progress
119
+ tier = report.quality_tier
120
+ status = report.valid? ? "✅" : "❌"
121
+ msg = " [#{index + 1}/#{files.size}] #{tier[:emoji]} #{report.error_count} errors #{status} #{shorten_path(file)}\n"
122
+ $stderr.print msg
123
+ $stderr.flush
124
+ end
125
+ end
126
+
127
+ reports
128
+ end
129
+
130
+ def sort_report(batch_report)
131
+ case options[:sort]
132
+ when :quality
133
+ batch_report.sort_by_quality
134
+ else
135
+ batch_report.sort_by_errors
136
+ end
137
+ end
138
+
139
+ def output_report(batch_report)
140
+ filtered = batch_report.filter_by_min_errors(options[:min_errors])
141
+ limited = filtered.limit(options[:limit])
142
+
143
+ formatter = case options[:format].to_sym
144
+ when :json
145
+ SvgQuality::Formatters::JsonFormatter.new(limited,
146
+ output: options[:output])
147
+ when :yaml
148
+ SvgQuality::Formatters::YamlFormatter.new(limited,
149
+ output: options[:output])
150
+ else
151
+ SvgQuality::Formatters::TerminalFormatter.new(limited,
152
+ output: options[:output], sort: options[:sort])
153
+ end
154
+
155
+ puts formatter.format
156
+ end
157
+
158
+ def shorten_path(path)
159
+ p = Pathname.new(path)
160
+ if p.absolute?
161
+ begin
162
+ p.relative_path_from(Pathname.pwd)
163
+ rescue StandardError
164
+ p
165
+ end
166
+ else
167
+ p
168
+ end.to_s
169
+ end
170
+ end
171
+ end
172
+ end
@@ -1,12 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "thor"
4
- require_relative "../eengine/wrapper"
5
- require_relative "../eengine_converter"
6
4
 
7
5
  module Suma
8
6
  module Cli
9
- # Command to compare EXPRESS schemas using eengine
10
7
  class Compare < Thor
11
8
  desc "compare TRIAL_SCHEMA REFERENCE_SCHEMA",
12
9
  "Compare EXPRESS schemas using eengine and generate Change YAML"
@@ -44,166 +41,17 @@ module Suma
44
41
  desc: "Enable verbose output"
45
42
 
46
43
  def compare(trial_schema, reference_schema)
47
- # Validate schema files exist
48
- unless File.exist?(trial_schema)
49
- say "Error: Trial schema not found: #{trial_schema}", :red
50
- exit 1
51
- end
52
-
53
- unless File.exist?(reference_schema)
54
- say "Error: Reference schema not found: #{reference_schema}", :red
55
- exit 1
56
- end
57
-
58
- # Check eengine availability
59
- unless Eengine::Wrapper.available?
60
- say "Error: eengine not found in PATH", :red
61
- say "Install eengine following instructions at:"
62
- say " macOS: https://github.com/expresslang/homebrew-eengine"
63
- say " Linux: https://github.com/expresslang/eengine-releases"
64
- exit 1
65
- end
44
+ comparer = SchemaComparer.new(trial_schema, reference_schema, options)
66
45
 
67
- # Auto-detect repo roots
68
- trial_stepmod = options[:trial_stepmod] ||
69
- detect_repo_root(trial_schema)
70
- reference_stepmod = options[:reference_stepmod] ||
71
- detect_repo_root(reference_schema)
46
+ result = comparer.compare
72
47
 
73
- if options[:verbose]
74
- say "Using eengine version: #{Eengine::Wrapper.version}", :green
75
- say "Trial repo root: #{trial_stepmod}", :cyan
76
- say "Reference repo root: #{reference_stepmod}", :cyan
77
- end
78
-
79
- # Create a temporary directory for eengine output
80
- require "tmpdir"
81
- out_dir = nil
82
- out_dir = Dir.mktmpdir("eengine-compare-")
83
-
84
- # Run comparison
85
- result = Eengine::Wrapper.compare(
86
- trial_schema,
87
- reference_schema,
88
- mode: options[:mode],
89
- trial_stepmod: trial_stepmod,
90
- reference_stepmod: reference_stepmod,
91
- out_dir: out_dir,
92
- )
93
-
94
- unless result[:has_changes]
48
+ if result.nil?
95
49
  say "No changes detected between schemas", :yellow
96
- # Clean up temp directory
97
- FileUtils.rm_rf(out_dir) if out_dir && File.directory?(out_dir)
98
- return
99
- end
100
-
101
- unless result[:xml_path]
102
- say "Error: XML output not found", :red
103
- exit 1
104
- end
105
-
106
- if options[:verbose]
107
- say "Comparison XML generated: #{result[:xml_path]}", :green
108
- end
109
-
110
- # Convert to Change YAML
111
- convert_to_change_yaml(result[:xml_path], trial_schema, out_dir)
112
- rescue Eengine::EengineError => e
113
- # Clean up temp directory
114
- FileUtils.rm_rf(out_dir) if out_dir && File.directory?(out_dir)
115
- say "Error: #{e.message}", :red
116
- say e.stderr if e.respond_to?(:stderr) && options[:verbose]
117
- exit 1
118
- end
119
-
120
- private
121
-
122
- def detect_repo_root(schema_path)
123
- # Walk up from schema path to find .git directory
124
- current = File.expand_path(File.dirname(schema_path))
125
-
126
- loop do
127
- if File.directory?(File.join(current, ".git"))
128
- return current
129
- end
130
-
131
- parent = File.dirname(current)
132
- break if parent == current # reached root
133
-
134
- current = parent
135
- end
136
-
137
- # If no .git found, use the directory containing the schema
138
- # (for non-git workflows)
139
- File.dirname(schema_path)
140
- end
141
-
142
- def convert_to_change_yaml(xml_path, trial_schema, out_dir)
143
- schema_name = extract_schema_name(trial_schema)
144
- output_path = determine_output_path(trial_schema)
145
-
146
- # Load existing ChangeSchema if it exists
147
- existing_schema = if File.exist?(output_path)
148
- if options[:verbose]
149
- say "Loading existing change schema: " \
150
- "#{output_path}", :cyan
151
- end
152
- require "expressir/changes"
153
- Expressir::Changes::SchemaChange.from_file(output_path)
154
- end
155
-
156
- # Convert using Suma's converter
157
- converter = EengineConverter.new(xml_path, schema_name)
158
- change_schema = converter.convert(
159
- version: options[:version],
160
- existing_change_schema: existing_schema,
161
- )
162
-
163
- # Save using Expressir model
164
- change_schema.to_file(output_path)
165
-
166
- # Determine what action was taken
167
- if existing_schema
168
- existing_version = existing_schema.versions.find do |ed|
169
- ed.version == options[:version]
170
- end
171
-
172
- say "Change YAML file updated: #{output_path}", :green
173
- if existing_version
174
- say " Replaced existing version #{options[:version]}", :green
175
- else
176
- say " Added version #{options[:version]} to change versions",
177
- :green
178
- end
179
- else
180
- say "Change YAML file created: #{output_path}", :green
181
- end
182
-
183
- if options[:verbose]
184
- say "\nGenerated change schema content:", :cyan
185
- say File.read(output_path)
186
- end
187
-
188
- # Clean up temp directory and XML file
189
- FileUtils.rm_rf(out_dir) if out_dir && File.directory?(out_dir)
190
- end
191
-
192
- def extract_schema_name(path)
193
- # Remove version suffix if present (e.g., schema_1.exp -> schema)
194
- basename = File.basename(path, ".exp")
195
- basename.sub(/_\d+$/, "")
196
- end
197
-
198
- def determine_output_path(trial_schema)
199
- if options[:output]
200
- options[:output]
201
50
  else
202
- # Place .changes.yaml next to the trial schema in the NEW repo
203
- base = extract_schema_name(trial_schema)
204
- dir = File.dirname(trial_schema)
205
- File.join(dir, "#{base}.changes.yaml")
51
+ say "Change YAML file: #{result}", :green
206
52
  end
53
+ rescue Suma::Error => e
54
+ raise Thor::Error, e.message
207
55
  end
208
56
  end
209
57
  end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "thor"
4
- require_relative "../thor_ext"
5
- require_relative "../jsdai"
6
4
  require "fileutils"
7
5
 
8
6
  module Suma