ukiryu 0.1.1 → 0.1.3

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +58 -14
  3. data/.gitignore +3 -0
  4. data/.rubocop_todo.yml +170 -79
  5. data/Gemfile +1 -1
  6. data/README.adoc +1603 -576
  7. data/docs/.gitignore +1 -0
  8. data/docs/Gemfile +10 -0
  9. data/docs/INDEX.adoc +261 -0
  10. data/docs/_config.yml +180 -0
  11. data/docs/advanced/custom-tool-classes.adoc +581 -0
  12. data/docs/advanced/index.adoc +20 -0
  13. data/docs/features/configuration.adoc +657 -0
  14. data/docs/features/index.adoc +31 -0
  15. data/docs/features/platform-support.adoc +488 -0
  16. data/docs/getting-started/core-concepts.adoc +666 -0
  17. data/docs/getting-started/index.adoc +36 -0
  18. data/docs/getting-started/installation.adoc +216 -0
  19. data/docs/getting-started/quick-start.adoc +258 -0
  20. data/docs/guides/env-var-sets.adoc +388 -0
  21. data/docs/guides/index.adoc +20 -0
  22. data/docs/interfaces/cli.adoc +609 -0
  23. data/docs/interfaces/index.adoc +153 -0
  24. data/docs/interfaces/ruby-api.adoc +538 -0
  25. data/docs/lychee.toml +49 -0
  26. data/docs/reference/configuration-options.adoc +720 -0
  27. data/docs/reference/error-codes.adoc +634 -0
  28. data/docs/reference/index.adoc +20 -0
  29. data/docs/reference/ruby-api.adoc +1217 -0
  30. data/docs/understanding/index.adoc +20 -0
  31. data/lib/ukiryu/cli.rb +43 -58
  32. data/lib/ukiryu/cli_commands/base_command.rb +16 -27
  33. data/lib/ukiryu/cli_commands/cache_command.rb +100 -0
  34. data/lib/ukiryu/cli_commands/commands_command.rb +8 -8
  35. data/lib/ukiryu/cli_commands/commands_command.rb.fixed +1 -1
  36. data/lib/ukiryu/cli_commands/config_command.rb +49 -7
  37. data/lib/ukiryu/cli_commands/definitions_command.rb +254 -0
  38. data/lib/ukiryu/cli_commands/describe_command.rb +13 -7
  39. data/lib/ukiryu/cli_commands/describe_command.rb.fixed +1 -1
  40. data/lib/ukiryu/cli_commands/docs_command.rb +148 -0
  41. data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +1 -1
  42. data/lib/ukiryu/cli_commands/extract_command.rb +2 -2
  43. data/lib/ukiryu/cli_commands/info_command.rb +7 -7
  44. data/lib/ukiryu/cli_commands/lint_command.rb +167 -0
  45. data/lib/ukiryu/cli_commands/list_command.rb +6 -6
  46. data/lib/ukiryu/cli_commands/opts_command.rb +2 -2
  47. data/lib/ukiryu/cli_commands/opts_command.rb.fixed +1 -1
  48. data/lib/ukiryu/cli_commands/register_command.rb +144 -0
  49. data/lib/ukiryu/cli_commands/resolve_command.rb +124 -0
  50. data/lib/ukiryu/cli_commands/run_command.rb +38 -14
  51. data/lib/ukiryu/cli_commands/run_file_command.rb +2 -2
  52. data/lib/ukiryu/cli_commands/system_command.rb +50 -32
  53. data/lib/ukiryu/cli_commands/validate_command.rb +452 -51
  54. data/lib/ukiryu/cli_commands/which_command.rb +5 -5
  55. data/lib/ukiryu/command_builder.rb +81 -23
  56. data/lib/ukiryu/config/env_provider.rb +3 -3
  57. data/lib/ukiryu/config/env_schema.rb +6 -6
  58. data/lib/ukiryu/config.rb +11 -11
  59. data/lib/ukiryu/definition/definition_cache.rb +238 -0
  60. data/lib/ukiryu/definition/definition_composer.rb +257 -0
  61. data/lib/ukiryu/definition/definition_linter.rb +460 -0
  62. data/lib/ukiryu/definition/definition_validator.rb +320 -0
  63. data/lib/ukiryu/definition/discovery.rb +239 -0
  64. data/lib/ukiryu/definition/documentation_generator.rb +429 -0
  65. data/lib/ukiryu/definition/lint_issue.rb +168 -0
  66. data/lib/ukiryu/definition/loader.rb +139 -0
  67. data/lib/ukiryu/definition/metadata.rb +159 -0
  68. data/lib/ukiryu/definition/source.rb +87 -0
  69. data/lib/ukiryu/definition/sources/file.rb +138 -0
  70. data/lib/ukiryu/definition/sources/string.rb +88 -0
  71. data/lib/ukiryu/definition/validation_result.rb +158 -0
  72. data/lib/ukiryu/definition/version_resolver.rb +194 -0
  73. data/lib/ukiryu/definition.rb +40 -0
  74. data/lib/ukiryu/errors.rb +6 -0
  75. data/lib/ukiryu/execution_context.rb +11 -11
  76. data/lib/ukiryu/executor.rb +6 -0
  77. data/lib/ukiryu/extractors/extractor.rb +6 -5
  78. data/lib/ukiryu/extractors/help_parser.rb +13 -19
  79. data/lib/ukiryu/logger.rb +3 -1
  80. data/lib/ukiryu/models/command_definition.rb +3 -3
  81. data/lib/ukiryu/models/command_info.rb +1 -1
  82. data/lib/ukiryu/models/components.rb +1 -3
  83. data/lib/ukiryu/models/env_var_definition.rb +11 -3
  84. data/lib/ukiryu/models/flag_definition.rb +15 -0
  85. data/lib/ukiryu/models/option_definition.rb +7 -7
  86. data/lib/ukiryu/models/platform_profile.rb +6 -3
  87. data/lib/ukiryu/models/routing.rb +1 -1
  88. data/lib/ukiryu/models/tool_definition.rb +2 -4
  89. data/lib/ukiryu/models/tool_metadata.rb +6 -6
  90. data/lib/ukiryu/models/validation_result.rb +1 -1
  91. data/lib/ukiryu/models/version_compatibility.rb +6 -3
  92. data/lib/ukiryu/models/version_detection.rb +10 -1
  93. data/lib/ukiryu/{registry.rb → register.rb} +54 -38
  94. data/lib/ukiryu/register_auto_manager.rb +268 -0
  95. data/lib/ukiryu/schema_validator.rb +31 -10
  96. data/lib/ukiryu/shell/base.rb +18 -0
  97. data/lib/ukiryu/shell/bash.rb +19 -1
  98. data/lib/ukiryu/shell/cmd.rb +11 -1
  99. data/lib/ukiryu/shell/powershell.rb +11 -1
  100. data/lib/ukiryu/shell.rb +1 -1
  101. data/lib/ukiryu/tool.rb +107 -95
  102. data/lib/ukiryu/tool_index.rb +22 -22
  103. data/lib/ukiryu/tools/base.rb +12 -25
  104. data/lib/ukiryu/tools/generator.rb +7 -7
  105. data/lib/ukiryu/tools.rb +3 -3
  106. data/lib/ukiryu/type.rb +20 -5
  107. data/lib/ukiryu/version.rb +1 -1
  108. data/lib/ukiryu/version_detector.rb +21 -2
  109. data/lib/ukiryu.rb +6 -3
  110. data/ukiryu-proposal.md +41 -41
  111. data/ukiryu.gemspec +1 -0
  112. metadata +64 -8
  113. data/.gitmodules +0 -3
@@ -0,0 +1,257 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../models/tool_definition'
4
+
5
+ module Ukiryu
6
+ module Definition
7
+ # Compose tool definitions from multiple sources
8
+ #
9
+ # This class handles merging and composing tool definitions,
10
+ # allowing for profile inheritance and command mixins.
11
+ class DefinitionComposer
12
+ # Merge strategies
13
+ MERGE_STRATEGIES = %i[override replace prepend append].freeze
14
+
15
+ # Compose a definition with its includes/inherits
16
+ #
17
+ # @param definition [Models::ToolDefinition] the base definition
18
+ # @param loader [Object] the loader to use for loading dependencies
19
+ # @return [Models::ToolDefinition] the composed definition
20
+ def self.compose(definition, loader: nil)
21
+ @loader = loader || Loader
22
+
23
+ # Process inherits first (base definitions)
24
+ definition = process_inherits(definition) if definition.respond_to?(:inherits) && definition.inherits
25
+
26
+ # Process includes (additions)
27
+ definition = process_includes(definition) if definition.respond_to?(:includes) && definition.includes
28
+
29
+ definition
30
+ end
31
+
32
+ # Process inherits clauses
33
+ #
34
+ # @param definition [Models::ToolDefinition] the definition
35
+ # @return [Models::ToolDefinition] the merged definition
36
+ def self.process_inherits(definition)
37
+ inherits = definition.inherits || []
38
+ return definition if inherits.empty?
39
+
40
+ # Load base definitions and merge them
41
+ inherits.reverse.reduce(definition) do |current, inherit_spec|
42
+ base_def = load_base_definition(inherit_spec)
43
+ next current unless base_def
44
+
45
+ merge_definitions(base_def, current, strategy: :replace)
46
+ end
47
+ end
48
+
49
+ # Process includes clauses
50
+ #
51
+ # @param definition [Models::ToolDefinition] the definition
52
+ # @return [Models::ToolDefinition] the merged definition
53
+ def self.process_includes(definition)
54
+ includes = definition.includes || []
55
+ return definition if includes.empty?
56
+
57
+ # Load included definitions and merge them
58
+ includes.reduce(definition) do |current, include_spec|
59
+ include_def = load_base_definition(include_spec)
60
+ next current unless include_def
61
+
62
+ merge_definitions(current, include_def, strategy: :append)
63
+ end
64
+ end
65
+
66
+ # Load a base definition from a spec
67
+ #
68
+ # @param spec [Hash, String] the specification
69
+ # @return [Models::ToolDefinition, nil] the loaded definition
70
+ def self.load_base_definition(spec)
71
+ case spec
72
+ when String
73
+ # Just a tool name, find latest version
74
+ metadata = Discovery.find(spec)
75
+ metadata&.load_definition
76
+ when Hash
77
+ tool = spec[:tool] || spec['tool']
78
+ version = spec[:version] || spec['version']
79
+
80
+ metadata = if version
81
+ Discovery.find(tool, version)
82
+ else
83
+ Discovery.find(tool)
84
+ end
85
+
86
+ metadata&.load_definition
87
+ end
88
+ end
89
+
90
+ # Merge two definitions
91
+ #
92
+ # @param base [Models::ToolDefinition] the base definition
93
+ # @param addition [Models::ToolDefinition] the definition to merge in
94
+ # @param strategy [Symbol] the merge strategy
95
+ # @return [Models::ToolDefinition] the merged definition
96
+ def self.merge_definitions(base, addition, strategy: :override)
97
+ # Create a merged definition by deep copying and merging
98
+ merged = base.dup
99
+
100
+ # Merge profiles
101
+ if addition.profiles
102
+ merged.profiles ||= []
103
+
104
+ case strategy
105
+ when :replace
106
+ merged.profiles = addition.profiles.dup
107
+ when :override
108
+ # Merge by profile name, addition overrides base
109
+ merged.profiles = merge_profiles_by_name(base.profiles || [], addition.profiles)
110
+ when :append
111
+ # Append addition profiles
112
+ merged.profiles = (base.profiles || []) + addition.profiles
113
+ when :prepend
114
+ # Prepend addition profiles
115
+ merged.profiles = addition.profiles + (base.profiles || [])
116
+ end
117
+ end
118
+
119
+ # Merge commands if profiles have them
120
+ merged.profiles&.each do |profile|
121
+ # Find corresponding profile in addition
122
+ addition_profile = find_profile(addition.profiles, profile.name)
123
+ next unless addition_profile
124
+
125
+ # Merge commands
126
+ next unless addition_profile.commands
127
+
128
+ profile.commands ||= []
129
+ case strategy
130
+ when :replace
131
+ profile.commands = addition_profile.commands.dup
132
+ when :override
133
+ profile.commands = merge_commands_by_name(profile.commands, addition_profile.commands)
134
+ when :append
135
+ profile.commands = profile.commands + addition_profile.commands
136
+ when :prepend
137
+ profile.commands = addition_profile.commands + profile.commands
138
+ end
139
+ end
140
+
141
+ merged
142
+ end
143
+
144
+ # Merge profiles by name
145
+ #
146
+ # @param base_profiles [Array] base profiles
147
+ # @param addition_profiles [Array] profiles to merge
148
+ # @return [Array] merged profiles
149
+ def self.merge_profiles_by_name(base_profiles, addition_profiles)
150
+ merged = base_profiles.dup
151
+
152
+ addition_profiles.each do |addition_profile|
153
+ existing_index = merged.find_index { |p| p.name == addition_profile.name }
154
+
155
+ if existing_index
156
+ # Merge the profile content
157
+ existing = merged[existing_index]
158
+ merged[existing_index] = merge_profile_content(existing, addition_profile)
159
+ else
160
+ # Add new profile
161
+ merged << addition_profile.dup
162
+ end
163
+ end
164
+
165
+ merged
166
+ end
167
+
168
+ # Merge the content of two profiles
169
+ #
170
+ # @param base [Object] the base profile
171
+ # @param addition [Object] the profile to merge
172
+ # @return [Object] merged profile
173
+ def self.merge_profile_content(base, addition)
174
+ # Create a new profile that merges both
175
+ merged = base.dup
176
+
177
+ # Merge commands
178
+ merged.commands = merge_commands_by_name(base.commands || [], addition.commands) if addition.commands
179
+
180
+ # Merge other arrays if they exist
181
+ %i[environment options flags arguments].each do |attr|
182
+ if addition.respond_to?(attr) && addition.send(attr)
183
+ base_items = base.respond_to?(attr) ? base.send(attr) : []
184
+ merged.send("#{attr}=", base_items + addition.send(attr))
185
+ end
186
+ end
187
+
188
+ # Override scalars
189
+ %i[description option_style].each do |attr|
190
+ merged.send("#{attr}=", addition.send(attr)) if addition.respond_to?(attr) && !addition.send(attr).nil?
191
+ end
192
+
193
+ merged
194
+ end
195
+
196
+ # Merge commands by name
197
+ #
198
+ # @param base_commands [Array] base commands
199
+ # @param addition_commands [Array] commands to merge
200
+ # @return [Array] merged commands
201
+ def self.merge_commands_by_name(base_commands, addition_commands)
202
+ merged = base_commands.dup
203
+
204
+ addition_commands.each do |addition_command|
205
+ existing_index = merged.find_index { |c| c.name == addition_command.name }
206
+
207
+ if existing_index
208
+ # Override existing command
209
+ merged[existing_index] = addition_command.dup
210
+ else
211
+ # Add new command
212
+ merged << addition_command.dup
213
+ end
214
+ end
215
+
216
+ merged
217
+ end
218
+
219
+ # Find a profile by name
220
+ #
221
+ # @param profiles [Array] the profiles array
222
+ # @param name [String] the profile name
223
+ # @return [Object, nil] the profile or nil
224
+ def self.find_profile(profiles, name)
225
+ return nil unless profiles
226
+
227
+ profiles.find { |p| p.name == name }
228
+ end
229
+
230
+ # Validate that a definition can be composed
231
+ #
232
+ # @param definition [Models::ToolDefinition] the definition to validate
233
+ # @return [Array<String>] array of validation errors (empty if valid)
234
+ def self.validate_composable(definition)
235
+ errors = []
236
+
237
+ # Check inherits
238
+ if definition.respond_to?(:inherits) && definition.inherits
239
+ definition.inherits.each do |inherit_spec|
240
+ base_def = load_base_definition(inherit_spec)
241
+ errors << "Cannot inherit from '#{inherit_spec}': definition not found" unless base_def
242
+ end
243
+ end
244
+
245
+ # Check includes
246
+ if definition.respond_to?(:includes) && definition.includes
247
+ definition.includes.each do |include_spec|
248
+ include_def = load_base_definition(include_spec)
249
+ errors << "Cannot include '#{include_spec}': definition not found" unless include_def
250
+ end
251
+ end
252
+
253
+ errors
254
+ end
255
+ end
256
+ end
257
+ end