ukiryu 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/docs.yml +63 -0
- data/.github/workflows/links.yml +99 -0
- data/.github/workflows/rake.yml +19 -0
- data/.github/workflows/release.yml +27 -0
- data/.gitignore +18 -4
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +213 -0
- data/Gemfile +12 -8
- data/README.adoc +613 -0
- data/Rakefile +2 -2
- data/docs/assets/logo.svg +1 -0
- data/exe/ukiryu +11 -0
- data/lib/ukiryu/action/base.rb +77 -0
- data/lib/ukiryu/cache.rb +199 -0
- data/lib/ukiryu/cli.rb +133 -307
- data/lib/ukiryu/cli_commands/base_command.rb +155 -0
- data/lib/ukiryu/cli_commands/commands_command.rb +120 -0
- data/lib/ukiryu/cli_commands/commands_command.rb.fixed +40 -0
- data/lib/ukiryu/cli_commands/config_command.rb +249 -0
- data/lib/ukiryu/cli_commands/describe_command.rb +326 -0
- data/lib/ukiryu/cli_commands/describe_command.rb.fixed +254 -0
- data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +180 -0
- data/lib/ukiryu/cli_commands/extract_command.rb +84 -0
- data/lib/ukiryu/cli_commands/info_command.rb +156 -0
- data/lib/ukiryu/cli_commands/list_command.rb +70 -0
- data/lib/ukiryu/cli_commands/opts_command.rb +106 -0
- data/lib/ukiryu/cli_commands/opts_command.rb.fixed +105 -0
- data/lib/ukiryu/cli_commands/response_formatter.rb +240 -0
- data/lib/ukiryu/cli_commands/run_command.rb +375 -0
- data/lib/ukiryu/cli_commands/run_file_command.rb +215 -0
- data/lib/ukiryu/cli_commands/system_command.rb +90 -0
- data/lib/ukiryu/cli_commands/validate_command.rb +87 -0
- data/lib/ukiryu/cli_commands/version_command.rb +16 -0
- data/lib/ukiryu/cli_commands/which_command.rb +166 -0
- data/lib/ukiryu/command_builder.rb +205 -0
- data/lib/ukiryu/config/env_provider.rb +64 -0
- data/lib/ukiryu/config/env_schema.rb +63 -0
- data/lib/ukiryu/config/override_resolver.rb +68 -0
- data/lib/ukiryu/config/type_converter.rb +59 -0
- data/lib/ukiryu/config.rb +249 -0
- data/lib/ukiryu/errors.rb +3 -0
- data/lib/ukiryu/executable_locator.rb +114 -0
- data/lib/ukiryu/execution/command_info.rb +64 -0
- data/lib/ukiryu/execution/metadata.rb +97 -0
- data/lib/ukiryu/execution/output.rb +144 -0
- data/lib/ukiryu/execution/result.rb +194 -0
- data/lib/ukiryu/execution.rb +15 -0
- data/lib/ukiryu/execution_context.rb +251 -0
- data/lib/ukiryu/executor.rb +76 -493
- data/lib/ukiryu/extractors/base_extractor.rb +63 -0
- data/lib/ukiryu/extractors/extractor.rb +150 -0
- data/lib/ukiryu/extractors/help_parser.rb +188 -0
- data/lib/ukiryu/extractors/native_extractor.rb +47 -0
- data/lib/ukiryu/io.rb +196 -0
- data/lib/ukiryu/logger.rb +544 -0
- data/lib/ukiryu/models/argument.rb +28 -0
- data/lib/ukiryu/models/argument_definition.rb +119 -0
- data/lib/ukiryu/models/arguments.rb +113 -0
- data/lib/ukiryu/models/command_definition.rb +176 -0
- data/lib/ukiryu/models/command_info.rb +37 -0
- data/lib/ukiryu/models/components.rb +107 -0
- data/lib/ukiryu/models/env_var_definition.rb +30 -0
- data/lib/ukiryu/models/error_response.rb +41 -0
- data/lib/ukiryu/models/execution_metadata.rb +31 -0
- data/lib/ukiryu/models/execution_report.rb +236 -0
- data/lib/ukiryu/models/exit_codes.rb +74 -0
- data/lib/ukiryu/models/flag_definition.rb +67 -0
- data/lib/ukiryu/models/option_definition.rb +102 -0
- data/lib/ukiryu/models/output_info.rb +25 -0
- data/lib/ukiryu/models/platform_profile.rb +153 -0
- data/lib/ukiryu/models/routing.rb +211 -0
- data/lib/ukiryu/models/search_paths.rb +39 -0
- data/lib/ukiryu/models/success_response.rb +85 -0
- data/lib/ukiryu/models/tool_definition.rb +145 -0
- data/lib/ukiryu/models/tool_metadata.rb +82 -0
- data/lib/ukiryu/models/validation_result.rb +80 -0
- data/lib/ukiryu/models/version_compatibility.rb +152 -0
- data/lib/ukiryu/models/version_detection.rb +39 -0
- data/lib/ukiryu/models.rb +23 -0
- data/lib/ukiryu/options/base.rb +95 -0
- data/lib/ukiryu/options_builder/formatter.rb +87 -0
- data/lib/ukiryu/options_builder/validator.rb +43 -0
- data/lib/ukiryu/options_builder.rb +311 -0
- data/lib/ukiryu/platform.rb +6 -6
- data/lib/ukiryu/registry.rb +143 -183
- data/lib/ukiryu/response/base.rb +217 -0
- data/lib/ukiryu/runtime.rb +179 -0
- data/lib/ukiryu/schema_validator.rb +8 -10
- data/lib/ukiryu/shell/bash.rb +3 -3
- data/lib/ukiryu/shell/cmd.rb +4 -4
- data/lib/ukiryu/shell/fish.rb +1 -1
- data/lib/ukiryu/shell/powershell.rb +3 -3
- data/lib/ukiryu/shell/sh.rb +1 -1
- data/lib/ukiryu/shell/zsh.rb +1 -1
- data/lib/ukiryu/shell.rb +146 -39
- data/lib/ukiryu/thor_ext.rb +208 -0
- data/lib/ukiryu/tool.rb +649 -258
- data/lib/ukiryu/tool_index.rb +224 -0
- data/lib/ukiryu/tools/base.rb +381 -0
- data/lib/ukiryu/tools/class_generator.rb +132 -0
- data/lib/ukiryu/tools/executable_finder.rb +29 -0
- data/lib/ukiryu/tools/generator.rb +154 -0
- data/lib/ukiryu/tools.rb +109 -0
- data/lib/ukiryu/type.rb +28 -43
- data/lib/ukiryu/validation/constraints.rb +281 -0
- data/lib/ukiryu/validation/validator.rb +188 -0
- data/lib/ukiryu/validation.rb +21 -0
- data/lib/ukiryu/version.rb +1 -1
- data/lib/ukiryu/version_detector.rb +51 -0
- data/lib/ukiryu.rb +31 -15
- data/ukiryu-proposal.md +2952 -0
- data/ukiryu.gemspec +18 -14
- metadata +137 -5
- data/.github/workflows/test.yml +0 -143
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_command'
|
|
4
|
+
require_relative '../tool'
|
|
5
|
+
|
|
6
|
+
module Ukiryu
|
|
7
|
+
module CliCommands
|
|
8
|
+
# Show comprehensive documentation for a tool or specific command
|
|
9
|
+
class DescribeCommand < BaseCommand
|
|
10
|
+
# Execute the describe command
|
|
11
|
+
#
|
|
12
|
+
# @param tool_name [String] the tool name
|
|
13
|
+
# @param command_name [String, nil] optional command name
|
|
14
|
+
def run(tool_name, command_name = nil)
|
|
15
|
+
setup_registry
|
|
16
|
+
|
|
17
|
+
# Use find_by for interface-based discovery (ping -> ping_bsd/ping_gnu)
|
|
18
|
+
tool = Tool.find_by(tool_name.to_sym)
|
|
19
|
+
error!("Tool not found: #{tool_name}\nAvailable tools: #{Registry.tools.sort.join(', ')}") unless tool
|
|
20
|
+
|
|
21
|
+
tool_commands = tool.commands
|
|
22
|
+
error! "No commands defined for #{tool_name}" unless tool_commands
|
|
23
|
+
|
|
24
|
+
# Special handling for "help" command - show tool-level help
|
|
25
|
+
if command_name&.to_s == 'help'
|
|
26
|
+
show_tool_help(tool, tool_commands)
|
|
27
|
+
return
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# If no command specified, show overview of all commands
|
|
31
|
+
unless command_name
|
|
32
|
+
describe_tool_overview(tool, tool_commands)
|
|
33
|
+
return
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Find the specific command
|
|
37
|
+
cmd = tool_commands.find { |c| c.name.to_s == command_name.to_s || c.name.to_sym == command_name.to_sym }
|
|
38
|
+
error! "Command '#{command_name}' not found for #{tool_name}\nAvailable commands: #{tool_commands.map(&:name).join(', ')}" unless cmd
|
|
39
|
+
|
|
40
|
+
describe_command(tool, tool_name, command_name, cmd)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Show tool-level help (similar to exec help)
|
|
46
|
+
def show_tool_help(tool, tool_commands)
|
|
47
|
+
profile = tool.profile
|
|
48
|
+
|
|
49
|
+
say '', :clear
|
|
50
|
+
say '=' * 60, :cyan
|
|
51
|
+
say "Tool: #{profile.name || tool.name}", :cyan
|
|
52
|
+
say '=' * 60, :cyan
|
|
53
|
+
say '', :clear
|
|
54
|
+
|
|
55
|
+
# Basic info
|
|
56
|
+
say "Display Name: #{profile.display_name || 'N/A'}", :white
|
|
57
|
+
say "Version: #{profile.version || 'N/A'}", :white
|
|
58
|
+
say "Homepage: #{profile.homepage || 'N/A'}", :white
|
|
59
|
+
say "Aliases: #{profile.aliases.join(', ')}", :white if profile.aliases && !profile.aliases.empty?
|
|
60
|
+
|
|
61
|
+
# Availability
|
|
62
|
+
say '', :clear
|
|
63
|
+
if tool.available?
|
|
64
|
+
say 'Status: INSTALLED', :green
|
|
65
|
+
say "Executable: #{tool.executable}", :white
|
|
66
|
+
say "Detected Version: #{tool.version || 'unknown'}", :white
|
|
67
|
+
else
|
|
68
|
+
say 'Status: NOT FOUND', :red
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Commands
|
|
72
|
+
say '', :clear
|
|
73
|
+
say "Commands (#{tool_commands.count}):", :yellow
|
|
74
|
+
tool_commands.each do |cmd|
|
|
75
|
+
cmd_display = (cmd.name || 'unnamed').to_s.ljust(20)
|
|
76
|
+
desc_display = cmd.description || 'No description'
|
|
77
|
+
say " #{cmd_display} #{desc_display}", :white
|
|
78
|
+
|
|
79
|
+
# Show usage if available
|
|
80
|
+
say " Usage: #{cmd.usage}", :dim if cmd.usage
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
say '', :clear
|
|
84
|
+
say "Usage: ukiryu exec #{tool.name} <command> [KEY=VALUE ...]", :dim
|
|
85
|
+
say " or: ukiryu exec #{tool.name} help", :dim
|
|
86
|
+
say " or: ukiryu describe #{tool.name} <command>", :dim
|
|
87
|
+
say '', :clear
|
|
88
|
+
say 'For more information on a specific command:', :dim
|
|
89
|
+
say " ukiryu opts #{tool.name} <command>", :dim
|
|
90
|
+
say " ukiryu describe #{tool.name} <command>", :dim
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Describe tool overview with all commands
|
|
94
|
+
def describe_tool_overview(tool, tool_commands)
|
|
95
|
+
profile = tool.profile
|
|
96
|
+
|
|
97
|
+
say '', :clear
|
|
98
|
+
say '=' * 60, :cyan
|
|
99
|
+
say "Tool: #{profile.name || tool.name}", :cyan
|
|
100
|
+
say '=' * 60, :cyan
|
|
101
|
+
say '', :clear
|
|
102
|
+
|
|
103
|
+
# Basic info
|
|
104
|
+
say "Display Name: #{profile.display_name || 'N/A'}", :white
|
|
105
|
+
say "Version: #{profile.version || 'N/A'}", :white
|
|
106
|
+
say "Homepage: #{profile.homepage || 'N/A'}", :white
|
|
107
|
+
say "Aliases: #{profile.aliases.join(', ')}", :white if profile.aliases && !profile.aliases.empty?
|
|
108
|
+
|
|
109
|
+
# Availability
|
|
110
|
+
say '', :clear
|
|
111
|
+
if tool.available?
|
|
112
|
+
say 'Status: INSTALLED', :green
|
|
113
|
+
say "Executable: #{tool.executable}", :white
|
|
114
|
+
say "Detected Version: #{tool.version || 'unknown'}", :white
|
|
115
|
+
else
|
|
116
|
+
say 'Status: NOT FOUND', :red
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Commands
|
|
120
|
+
say '', :clear
|
|
121
|
+
say "Commands (#{tool_commands.count}):", :yellow
|
|
122
|
+
tool_commands.each do |cmd|
|
|
123
|
+
cmd_display = (cmd.name || 'unnamed').to_s.ljust(20)
|
|
124
|
+
desc_display = cmd.description || 'No description'
|
|
125
|
+
say " #{cmd_display} #{desc_display}", :white
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
say '', :clear
|
|
129
|
+
say "Use 'ukiryu describe #{tool.name} <command>' for detailed command documentation", :dim
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Describe a specific command with all options, types, and option sets
|
|
133
|
+
def describe_command(tool, tool_name, command_name, cmd)
|
|
134
|
+
say '', :clear
|
|
135
|
+
say '=' * 60, :cyan
|
|
136
|
+
say "#{tool.name} #{command_name}", :cyan
|
|
137
|
+
say '=' * 60, :cyan
|
|
138
|
+
say '', :clear
|
|
139
|
+
|
|
140
|
+
# Description and usage
|
|
141
|
+
say cmd.description if cmd.description
|
|
142
|
+
say '', :clear
|
|
143
|
+
|
|
144
|
+
# Usage
|
|
145
|
+
if cmd.usage
|
|
146
|
+
say 'Usage:', :yellow
|
|
147
|
+
say " #{cmd.usage}", :white
|
|
148
|
+
say '', :clear
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Subcommand
|
|
152
|
+
if cmd.subcommand
|
|
153
|
+
say "Subcommand: #{cmd.subcommand}", :white
|
|
154
|
+
say '', :clear
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Execution mode
|
|
158
|
+
if cmd.execution_mode
|
|
159
|
+
say "Execution Mode: #{cmd.execution_mode}", :white
|
|
160
|
+
say '', :clear
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Arguments
|
|
164
|
+
if cmd.arguments && !cmd.arguments.empty?
|
|
165
|
+
say 'Arguments:', :yellow
|
|
166
|
+
cmd.arguments.each do |arg|
|
|
167
|
+
name = arg.name || 'unnamed'
|
|
168
|
+
type = arg.type || 'string'
|
|
169
|
+
required = arg.required ? 'required' : 'optional'
|
|
170
|
+
variadic = arg.variadic ? '(variadic)' : ''
|
|
171
|
+
position = arg.position || 'default'
|
|
172
|
+
|
|
173
|
+
say " #{name} (#{type}, #{required}#{variadic})", :white
|
|
174
|
+
say " Position: #{position}", :dim if position != 'default'
|
|
175
|
+
say " Description: #{arg.description}", :dim if arg.description
|
|
176
|
+
|
|
177
|
+
# Type constraints
|
|
178
|
+
if arg.min || arg.max || arg.size
|
|
179
|
+
constraints = []
|
|
180
|
+
constraints << "min: #{arg.min}" if arg.min
|
|
181
|
+
constraints << "max: #{arg.max}" if arg.max
|
|
182
|
+
constraints << "size: #{arg.size.inspect}" if arg.size
|
|
183
|
+
say " Constraints: #{constraints.join(', ')}", :dim
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Range
|
|
187
|
+
say " Range: #{arg.range.join('..')}", :dim if arg.range
|
|
188
|
+
|
|
189
|
+
# Valid values
|
|
190
|
+
say " Valid values: #{arg.values.join(', ')}", :dim if arg.values
|
|
191
|
+
|
|
192
|
+
say '', :clear
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Options
|
|
197
|
+
if cmd.options && !cmd.options.empty?
|
|
198
|
+
say 'Options:', :yellow
|
|
199
|
+
cmd.options.each do |opt|
|
|
200
|
+
name = opt.name || 'unnamed'
|
|
201
|
+
cli = opt.cli || 'N/A'
|
|
202
|
+
type = opt.type || 'string'
|
|
203
|
+
format = opt.format || 'N/A'
|
|
204
|
+
default = opt.default
|
|
205
|
+
platforms = opt.platforms || []
|
|
206
|
+
|
|
207
|
+
say " #{name} (#{type})", :white
|
|
208
|
+
say " CLI: #{cli}", :dim
|
|
209
|
+
say " Format: #{format}", :dim if format != 'N/A'
|
|
210
|
+
say " Default: #{default}", :dim if default
|
|
211
|
+
say " Platforms: #{platforms.join(', ')}", :dim if platforms.any?
|
|
212
|
+
say " Description: #{opt.description}", :dim if opt.description
|
|
213
|
+
|
|
214
|
+
# Type constraints
|
|
215
|
+
say " Range: #{opt.range.join('..')}", :dim if opt.range
|
|
216
|
+
|
|
217
|
+
# Valid values (for symbol type)
|
|
218
|
+
say " Valid values: #{opt.values.join(', ')}", :dim if opt.values
|
|
219
|
+
|
|
220
|
+
# Element type (for arrays)
|
|
221
|
+
say " Element type: #{opt.of}", :dim if opt.of
|
|
222
|
+
|
|
223
|
+
say '', :clear
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Post-options (options between input and output)
|
|
228
|
+
if cmd.post_options && !cmd.post_options.empty?
|
|
229
|
+
say 'Post-Options (between input and output):', :yellow
|
|
230
|
+
cmd.post_options.each do |opt|
|
|
231
|
+
name = opt.name || 'unnamed'
|
|
232
|
+
cli = opt.cli || 'N/A'
|
|
233
|
+
type = opt.type || 'string'
|
|
234
|
+
|
|
235
|
+
say " #{name} (#{type})", :white
|
|
236
|
+
say " CLI: #{cli}", :dim
|
|
237
|
+
say " Description: #{opt.description}", :dim if opt.description
|
|
238
|
+
say '', :clear
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Flags
|
|
243
|
+
if cmd.flags && !cmd.flags.empty?
|
|
244
|
+
say 'Flags:', :yellow
|
|
245
|
+
cmd.flags.each do |flag|
|
|
246
|
+
name = flag.name || 'unnamed'
|
|
247
|
+
cli = flag.cli || 'N/A'
|
|
248
|
+
default = flag.default
|
|
249
|
+
platforms = flag.platforms || []
|
|
250
|
+
|
|
251
|
+
say " #{name} (boolean)", :white
|
|
252
|
+
say " CLI: #{cli}", :dim
|
|
253
|
+
say " Default: #{default}", :dim unless default.nil?
|
|
254
|
+
say " Platforms: #{platforms.join(', ')}", :dim if platforms.any?
|
|
255
|
+
say " Description: #{flag.description}", :dim if flag.description
|
|
256
|
+
say '', :clear
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Environment variables
|
|
261
|
+
if cmd.env_vars && !cmd.env_vars.empty?
|
|
262
|
+
say 'Environment Variables:', :yellow
|
|
263
|
+
cmd.env_vars.each do |ev|
|
|
264
|
+
name = ev.name || 'unnamed'
|
|
265
|
+
value = ev.value
|
|
266
|
+
env_var = ev.env_var
|
|
267
|
+
platforms = ev.platforms || []
|
|
268
|
+
|
|
269
|
+
say " #{name}", :white
|
|
270
|
+
say " Value: #{value.inspect}", :dim if value
|
|
271
|
+
say " From env var: #{env_var}", :dim if env_var
|
|
272
|
+
say " Platforms: #{platforms.join(', ')}", :dim if platforms.any?
|
|
273
|
+
say '', :clear
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Option sets (commonly used option combinations)
|
|
278
|
+
say 'Option Sets (common combinations):', :yellow
|
|
279
|
+
say ' --help (show help)', :dim
|
|
280
|
+
say ' --version (show version)', :dim
|
|
281
|
+
|
|
282
|
+
# Group related options by function
|
|
283
|
+
if cmd.options && !cmd.options.empty?
|
|
284
|
+
output_opts = cmd.options.select { |o| o.name =~ /output|out|file|format/i }
|
|
285
|
+
if output_opts.any?
|
|
286
|
+
say '', :clear
|
|
287
|
+
say "Output options: #{output_opts.map(&:name).join(', ')}", :dim
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
quality_opts = cmd.options.select { |o| o.name =~ /quality|q|compression/i }
|
|
291
|
+
say "Quality options: #{quality_opts.map(&:name).join(', ')}", :dim if quality_opts.any?
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Exit codes
|
|
295
|
+
# Exit codes are bound to individual commands/actions
|
|
296
|
+
exit_codes = cmd.exit_codes
|
|
297
|
+
if exit_codes && (exit_codes.standard_codes&.any? || exit_codes.custom_codes&.any?)
|
|
298
|
+
say '', :clear
|
|
299
|
+
say 'Exit Codes:', :yellow
|
|
300
|
+
|
|
301
|
+
standard_codes = exit_codes.standard_codes
|
|
302
|
+
if standard_codes&.any?
|
|
303
|
+
say ' Standard:', :dim
|
|
304
|
+
standard_codes.sort_by { |k, _v| k.to_i }.each do |code, meaning|
|
|
305
|
+
say " #{code.rjust(3)}: #{meaning}", :white
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
custom_codes = exit_codes.custom_codes
|
|
310
|
+
if custom_codes&.any?
|
|
311
|
+
say ' Custom:', :dim
|
|
312
|
+
custom_codes.sort_by { |k, _v| k.to_i }.each do |code, meaning|
|
|
313
|
+
say " #{code.rjust(3)}: #{meaning}", :white
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
say '', :clear
|
|
319
|
+
say '=' * 60, :cyan
|
|
320
|
+
say 'Example usage:', :yellow
|
|
321
|
+
say " ukiryu exec #{tool_name} #{command_name} key=value", :white
|
|
322
|
+
say '=' * 60, :cyan
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_command'
|
|
4
|
+
require_relative '../tool'
|
|
5
|
+
|
|
6
|
+
module Ukiryu
|
|
7
|
+
module CliCommands
|
|
8
|
+
# Show comprehensive documentation for a tool or specific command
|
|
9
|
+
class DescribeCommand < BaseCommand
|
|
10
|
+
# Execute the describe command
|
|
11
|
+
#
|
|
12
|
+
# @param tool_name [String] the tool name
|
|
13
|
+
# @param command_name [String, nil] optional command name
|
|
14
|
+
def run(tool_name, command_name = nil)
|
|
15
|
+
setup_registry
|
|
16
|
+
|
|
17
|
+
tool = Tool.get(tool_name)
|
|
18
|
+
tool_commands = tool.commands
|
|
19
|
+
|
|
20
|
+
error! "No commands defined for #{tool_name}" unless tool_commands
|
|
21
|
+
|
|
22
|
+
# If no command specified, show overview of all commands
|
|
23
|
+
unless command_name
|
|
24
|
+
describe_tool_overview(tool, tool_commands)
|
|
25
|
+
return
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Find the specific command
|
|
29
|
+
cmd = tool_commands[command_name.to_sym] || tool_commands[command_name]
|
|
30
|
+
error! "Command '#{command_name}' not found for #{tool_name}\nAvailable commands: #{tool_commands.keys.join(', ')}" unless cmd
|
|
31
|
+
|
|
32
|
+
describe_command(tool, tool_name, command_name, cmd)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Describe tool overview with all commands
|
|
38
|
+
def describe_tool_overview(tool, tool_commands)
|
|
39
|
+
profile = tool.profile
|
|
40
|
+
|
|
41
|
+
say '', :clear
|
|
42
|
+
say '=' * 60, :cyan
|
|
43
|
+
say "Tool: #{profile.name || tool.name}", :cyan
|
|
44
|
+
say '=' * 60, :cyan
|
|
45
|
+
say '', :clear
|
|
46
|
+
|
|
47
|
+
# Basic info
|
|
48
|
+
say "Display Name: #{profile.display_name || 'N/A'}", :white
|
|
49
|
+
say "Version: #{profile.version || 'N/A'}", :white
|
|
50
|
+
say "Homepage: #{profile.homepage || 'N/A'}", :white
|
|
51
|
+
say "Aliases: #{profile.aliases.join(', ')}", :white if profile.aliases && !profile.aliases.empty?
|
|
52
|
+
|
|
53
|
+
# Availability
|
|
54
|
+
say '', :clear
|
|
55
|
+
if tool.available?
|
|
56
|
+
say 'Status: INSTALLED', :green
|
|
57
|
+
say "Executable: #{tool.executable}", :white
|
|
58
|
+
say "Detected Version: #{tool.version || 'unknown'}", :white
|
|
59
|
+
else
|
|
60
|
+
say 'Status: NOT FOUND', :red
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Commands
|
|
64
|
+
say '', :clear
|
|
65
|
+
say "Commands (#{tool_commands.count}):", :yellow
|
|
66
|
+
tool_commands.each do |cmd_name, cmd|
|
|
67
|
+
cmd_display = cmd_name.to_s.ljust(20)
|
|
68
|
+
desc_display = cmd.description || 'No description'
|
|
69
|
+
say " #{cmd_display} #{desc_display}", :white
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
say '', :clear
|
|
73
|
+
say "Use 'ukiryu describe #{tool.name} <command>' for detailed command documentation", :dim
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Describe a specific command with all options, types, and option sets
|
|
77
|
+
def describe_command(tool, tool_name, command_name, cmd)
|
|
78
|
+
say '', :clear
|
|
79
|
+
say '=' * 60, :cyan
|
|
80
|
+
say "#{tool.name} #{command_name}", :cyan
|
|
81
|
+
say '=' * 60, :cyan
|
|
82
|
+
say '', :clear
|
|
83
|
+
|
|
84
|
+
# Description and usage
|
|
85
|
+
say cmd.description if cmd.description
|
|
86
|
+
say '', :clear
|
|
87
|
+
|
|
88
|
+
# Usage
|
|
89
|
+
if cmd.usage
|
|
90
|
+
say 'Usage:', :yellow
|
|
91
|
+
say " #{cmd.usage}", :white
|
|
92
|
+
say '', :clear
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Subcommand
|
|
96
|
+
if cmd.subcommand
|
|
97
|
+
say "Subcommand: #{cmd.subcommand}", :white
|
|
98
|
+
say '', :clear
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Execution mode
|
|
102
|
+
if cmd.execution_mode
|
|
103
|
+
say "Execution Mode: #{cmd.execution_mode}", :white
|
|
104
|
+
say '', :clear
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Arguments
|
|
108
|
+
if cmd.arguments && !cmd.arguments.empty?
|
|
109
|
+
say 'Arguments:', :yellow
|
|
110
|
+
cmd.arguments.each do |arg|
|
|
111
|
+
name = arg.name || 'unnamed'
|
|
112
|
+
type = arg.type || 'string'
|
|
113
|
+
required = arg.required ? 'required' : 'optional'
|
|
114
|
+
variadic = arg.variadic ? '(variadic)' : ''
|
|
115
|
+
position = arg.position || 'default'
|
|
116
|
+
|
|
117
|
+
say " #{name} (#{type}, #{required}#{variadic})", :white
|
|
118
|
+
say " Position: #{position}", :dim if position != 'default'
|
|
119
|
+
say " Description: #{arg.description}", :dim if arg.description
|
|
120
|
+
|
|
121
|
+
# Type constraints
|
|
122
|
+
if arg.min || arg.max || arg.size
|
|
123
|
+
constraints = []
|
|
124
|
+
constraints << "min: #{arg.min}" if arg.min
|
|
125
|
+
constraints << "max: #{arg.max}" if arg.max
|
|
126
|
+
constraints << "size: #{arg.size.inspect}" if arg.size
|
|
127
|
+
say " Constraints: #{constraints.join(', ')}", :dim
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Range
|
|
131
|
+
say " Range: #{arg.range.join('..')}", :dim if arg.range
|
|
132
|
+
|
|
133
|
+
# Valid values
|
|
134
|
+
say " Valid values: #{arg.values.join(', ')}", :dim if arg.values
|
|
135
|
+
|
|
136
|
+
say '', :clear
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Options
|
|
141
|
+
if cmd.options && !cmd.options.empty?
|
|
142
|
+
say 'Options:', :yellow
|
|
143
|
+
cmd.options.each do |opt|
|
|
144
|
+
name = opt.name || 'unnamed'
|
|
145
|
+
cli = opt.cli || 'N/A'
|
|
146
|
+
type = opt.type || 'string'
|
|
147
|
+
format = opt.format || 'N/A'
|
|
148
|
+
default = opt.default
|
|
149
|
+
platforms = opt.platforms || []
|
|
150
|
+
|
|
151
|
+
say " #{name} (#{type})", :white
|
|
152
|
+
say " CLI: #{cli}", :dim
|
|
153
|
+
say " Format: #{format}", :dim if format != 'N/A'
|
|
154
|
+
say " Default: #{default}", :dim if default
|
|
155
|
+
say " Platforms: #{platforms.join(', ')}", :dim if platforms.any?
|
|
156
|
+
say " Description: #{opt.description}", :dim if opt.description
|
|
157
|
+
|
|
158
|
+
# Type constraints
|
|
159
|
+
if opt.min || opt.max || opt.range
|
|
160
|
+
constraints = []
|
|
161
|
+
constraints << "min: #{opt.min}" if opt.min
|
|
162
|
+
constraints << "max: #{opt.max}" if opt.max
|
|
163
|
+
constraints << "range: #{opt.range.join('..')}" if opt.range
|
|
164
|
+
say " Constraints: #{constraints.join(', ')}", :dim
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Valid values (for symbol type)
|
|
168
|
+
say " Valid values: #{opt.values.join(', ')}", :dim if opt.values
|
|
169
|
+
|
|
170
|
+
# Element type (for arrays)
|
|
171
|
+
say " Element type: #{opt.of}", :dim if opt.of
|
|
172
|
+
|
|
173
|
+
say '', :clear
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Post-options (options between input and output)
|
|
178
|
+
if cmd.post_options && !cmd.post_options.empty?
|
|
179
|
+
say 'Post-Options (between input and output):', :yellow
|
|
180
|
+
cmd.post_options.each do |opt|
|
|
181
|
+
name = opt.name || 'unnamed'
|
|
182
|
+
cli = opt.cli || 'N/A'
|
|
183
|
+
type = opt.type || 'string'
|
|
184
|
+
|
|
185
|
+
say " #{name} (#{type})", :white
|
|
186
|
+
say " CLI: #{cli}", :dim
|
|
187
|
+
say " Description: #{opt.description}", :dim if opt.description
|
|
188
|
+
say '', :clear
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Flags
|
|
193
|
+
if cmd.flags && !cmd.flags.empty?
|
|
194
|
+
say 'Flags:', :yellow
|
|
195
|
+
cmd.flags.each do |flag|
|
|
196
|
+
name = flag.name || 'unnamed'
|
|
197
|
+
cli = flag.cli || 'N/A'
|
|
198
|
+
default = flag.default
|
|
199
|
+
platforms = flag.platforms || []
|
|
200
|
+
|
|
201
|
+
say " #{name} (boolean)", :white
|
|
202
|
+
say " CLI: #{cli}", :dim
|
|
203
|
+
say " Default: #{default}", :dim unless default.nil?
|
|
204
|
+
say " Platforms: #{platforms.join(', ')}", :dim if platforms.any?
|
|
205
|
+
say " Description: #{flag.description}", :dim if flag.description
|
|
206
|
+
say '', :clear
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Environment variables
|
|
211
|
+
if cmd.env_vars && !cmd.env_vars.empty?
|
|
212
|
+
say 'Environment Variables:', :yellow
|
|
213
|
+
cmd.env_vars.each do |ev|
|
|
214
|
+
name = ev.name || 'unnamed'
|
|
215
|
+
value = ev.value
|
|
216
|
+
env_var = ev.env_var
|
|
217
|
+
platforms = ev.platforms || []
|
|
218
|
+
|
|
219
|
+
say " #{name}", :white
|
|
220
|
+
say " Value: #{value.inspect}", :dim if value
|
|
221
|
+
say " From env var: #{env_var}", :dim if env_var
|
|
222
|
+
say " Platforms: #{platforms.join(', ')}", :dim if platforms.any?
|
|
223
|
+
say '', :clear
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Option sets (commonly used option combinations)
|
|
228
|
+
say 'Option Sets (common combinations):', :yellow
|
|
229
|
+
say ' --help (show help)', :dim
|
|
230
|
+
say ' --version (show version)', :dim
|
|
231
|
+
|
|
232
|
+
# Group related options by function
|
|
233
|
+
if cmd.options && !cmd.options.empty?
|
|
234
|
+
output_opts = cmd.options.select { |o| o.name =~ /output|out|file|format/i }
|
|
235
|
+
if output_opts.any?
|
|
236
|
+
say '', :clear
|
|
237
|
+
say "Output options: #{output_opts.map(&:name).join(', ')}", :dim
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
quality_opts = cmd.options.select { |o| o.name =~ /quality|q|compression/i }
|
|
241
|
+
say "Quality options: #{quality_opts.map(&:name).join(', ')}", :dim if quality_opts.any?
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
say '', :clear
|
|
245
|
+
say '=' * 60, :cyan
|
|
246
|
+
say 'Example usage:', :yellow
|
|
247
|
+
say " ukiryu execute #{tool_name} #{command_name} [options...]", :white
|
|
248
|
+
say " ukiryu exec-inline #{tool_name} #{command_name} key=value", :white
|
|
249
|
+
say '=' * 60, :cyan
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|