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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +63 -0
  3. data/.github/workflows/links.yml +99 -0
  4. data/.github/workflows/rake.yml +19 -0
  5. data/.github/workflows/release.yml +27 -0
  6. data/.gitignore +18 -4
  7. data/.rubocop.yml +1 -0
  8. data/.rubocop_todo.yml +213 -0
  9. data/Gemfile +12 -8
  10. data/README.adoc +613 -0
  11. data/Rakefile +2 -2
  12. data/docs/assets/logo.svg +1 -0
  13. data/exe/ukiryu +11 -0
  14. data/lib/ukiryu/action/base.rb +77 -0
  15. data/lib/ukiryu/cache.rb +199 -0
  16. data/lib/ukiryu/cli.rb +133 -307
  17. data/lib/ukiryu/cli_commands/base_command.rb +155 -0
  18. data/lib/ukiryu/cli_commands/commands_command.rb +120 -0
  19. data/lib/ukiryu/cli_commands/commands_command.rb.fixed +40 -0
  20. data/lib/ukiryu/cli_commands/config_command.rb +249 -0
  21. data/lib/ukiryu/cli_commands/describe_command.rb +326 -0
  22. data/lib/ukiryu/cli_commands/describe_command.rb.fixed +254 -0
  23. data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +180 -0
  24. data/lib/ukiryu/cli_commands/extract_command.rb +84 -0
  25. data/lib/ukiryu/cli_commands/info_command.rb +156 -0
  26. data/lib/ukiryu/cli_commands/list_command.rb +70 -0
  27. data/lib/ukiryu/cli_commands/opts_command.rb +106 -0
  28. data/lib/ukiryu/cli_commands/opts_command.rb.fixed +105 -0
  29. data/lib/ukiryu/cli_commands/response_formatter.rb +240 -0
  30. data/lib/ukiryu/cli_commands/run_command.rb +375 -0
  31. data/lib/ukiryu/cli_commands/run_file_command.rb +215 -0
  32. data/lib/ukiryu/cli_commands/system_command.rb +90 -0
  33. data/lib/ukiryu/cli_commands/validate_command.rb +87 -0
  34. data/lib/ukiryu/cli_commands/version_command.rb +16 -0
  35. data/lib/ukiryu/cli_commands/which_command.rb +166 -0
  36. data/lib/ukiryu/command_builder.rb +205 -0
  37. data/lib/ukiryu/config/env_provider.rb +64 -0
  38. data/lib/ukiryu/config/env_schema.rb +63 -0
  39. data/lib/ukiryu/config/override_resolver.rb +68 -0
  40. data/lib/ukiryu/config/type_converter.rb +59 -0
  41. data/lib/ukiryu/config.rb +249 -0
  42. data/lib/ukiryu/errors.rb +3 -0
  43. data/lib/ukiryu/executable_locator.rb +114 -0
  44. data/lib/ukiryu/execution/command_info.rb +64 -0
  45. data/lib/ukiryu/execution/metadata.rb +97 -0
  46. data/lib/ukiryu/execution/output.rb +144 -0
  47. data/lib/ukiryu/execution/result.rb +194 -0
  48. data/lib/ukiryu/execution.rb +15 -0
  49. data/lib/ukiryu/execution_context.rb +251 -0
  50. data/lib/ukiryu/executor.rb +76 -493
  51. data/lib/ukiryu/extractors/base_extractor.rb +63 -0
  52. data/lib/ukiryu/extractors/extractor.rb +150 -0
  53. data/lib/ukiryu/extractors/help_parser.rb +188 -0
  54. data/lib/ukiryu/extractors/native_extractor.rb +47 -0
  55. data/lib/ukiryu/io.rb +196 -0
  56. data/lib/ukiryu/logger.rb +544 -0
  57. data/lib/ukiryu/models/argument.rb +28 -0
  58. data/lib/ukiryu/models/argument_definition.rb +119 -0
  59. data/lib/ukiryu/models/arguments.rb +113 -0
  60. data/lib/ukiryu/models/command_definition.rb +176 -0
  61. data/lib/ukiryu/models/command_info.rb +37 -0
  62. data/lib/ukiryu/models/components.rb +107 -0
  63. data/lib/ukiryu/models/env_var_definition.rb +30 -0
  64. data/lib/ukiryu/models/error_response.rb +41 -0
  65. data/lib/ukiryu/models/execution_metadata.rb +31 -0
  66. data/lib/ukiryu/models/execution_report.rb +236 -0
  67. data/lib/ukiryu/models/exit_codes.rb +74 -0
  68. data/lib/ukiryu/models/flag_definition.rb +67 -0
  69. data/lib/ukiryu/models/option_definition.rb +102 -0
  70. data/lib/ukiryu/models/output_info.rb +25 -0
  71. data/lib/ukiryu/models/platform_profile.rb +153 -0
  72. data/lib/ukiryu/models/routing.rb +211 -0
  73. data/lib/ukiryu/models/search_paths.rb +39 -0
  74. data/lib/ukiryu/models/success_response.rb +85 -0
  75. data/lib/ukiryu/models/tool_definition.rb +145 -0
  76. data/lib/ukiryu/models/tool_metadata.rb +82 -0
  77. data/lib/ukiryu/models/validation_result.rb +80 -0
  78. data/lib/ukiryu/models/version_compatibility.rb +152 -0
  79. data/lib/ukiryu/models/version_detection.rb +39 -0
  80. data/lib/ukiryu/models.rb +23 -0
  81. data/lib/ukiryu/options/base.rb +95 -0
  82. data/lib/ukiryu/options_builder/formatter.rb +87 -0
  83. data/lib/ukiryu/options_builder/validator.rb +43 -0
  84. data/lib/ukiryu/options_builder.rb +311 -0
  85. data/lib/ukiryu/platform.rb +6 -6
  86. data/lib/ukiryu/registry.rb +143 -183
  87. data/lib/ukiryu/response/base.rb +217 -0
  88. data/lib/ukiryu/runtime.rb +179 -0
  89. data/lib/ukiryu/schema_validator.rb +8 -10
  90. data/lib/ukiryu/shell/bash.rb +3 -3
  91. data/lib/ukiryu/shell/cmd.rb +4 -4
  92. data/lib/ukiryu/shell/fish.rb +1 -1
  93. data/lib/ukiryu/shell/powershell.rb +3 -3
  94. data/lib/ukiryu/shell/sh.rb +1 -1
  95. data/lib/ukiryu/shell/zsh.rb +1 -1
  96. data/lib/ukiryu/shell.rb +146 -39
  97. data/lib/ukiryu/thor_ext.rb +208 -0
  98. data/lib/ukiryu/tool.rb +649 -258
  99. data/lib/ukiryu/tool_index.rb +224 -0
  100. data/lib/ukiryu/tools/base.rb +381 -0
  101. data/lib/ukiryu/tools/class_generator.rb +132 -0
  102. data/lib/ukiryu/tools/executable_finder.rb +29 -0
  103. data/lib/ukiryu/tools/generator.rb +154 -0
  104. data/lib/ukiryu/tools.rb +109 -0
  105. data/lib/ukiryu/type.rb +28 -43
  106. data/lib/ukiryu/validation/constraints.rb +281 -0
  107. data/lib/ukiryu/validation/validator.rb +188 -0
  108. data/lib/ukiryu/validation.rb +21 -0
  109. data/lib/ukiryu/version.rb +1 -1
  110. data/lib/ukiryu/version_detector.rb +51 -0
  111. data/lib/ukiryu.rb +31 -15
  112. data/ukiryu-proposal.md +2952 -0
  113. data/ukiryu.gemspec +18 -14
  114. metadata +137 -5
  115. data/.github/workflows/test.yml +0 -143
data/exe/ukiryu ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Add lib directory to load path
5
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
6
+
7
+ # Require thor first before ukiryu to ensure proper loading
8
+ require 'thor'
9
+ require 'ukiryu'
10
+
11
+ Ukiryu::Cli.start(ARGV)
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ukiryu
4
+ module Action
5
+ # Abstract base class for action classes
6
+ #
7
+ # Action classes represent executable commands and provide a fluent
8
+ # interface for building and executing commands.
9
+ #
10
+ # This is an alternative pattern to using options objects directly.
11
+ #
12
+ # @abstract
13
+ class Base
14
+ class << self
15
+ # Get the command name
16
+ #
17
+ # @return [Symbol] the command name
18
+ attr_reader :command_name
19
+
20
+ # Get the command definition
21
+ #
22
+ # @return [Hash] the command definition
23
+ attr_reader :command_def
24
+
25
+ # Get the associated tool class
26
+ #
27
+ # @return [Class] the tool class
28
+ attr_reader :tool_class
29
+ end
30
+
31
+ # Create a new action
32
+ #
33
+ # @param tool_instance [Tools::Base] the tool instance to use
34
+ def initialize(tool_instance = nil)
35
+ @tool = tool_instance || self.class.tool_class.new
36
+ end
37
+
38
+ # Execute this action with options
39
+ #
40
+ # @param options [Hash, Options::Base] the options to use
41
+ # @return [Response::Base] the execution response
42
+ def run(options = {})
43
+ # If options is a hash, create an options object
44
+ if options.is_a?(Hash)
45
+ options_class = @tool.options_class_for(self.class.command_name)
46
+ options_obj = options_class.new
47
+ options_obj.set(options)
48
+ options = options_obj
49
+ end
50
+
51
+ # Execute with the tool
52
+ @tool.execute(self.class.command_name, options)
53
+ end
54
+
55
+ # Create an options object for this action
56
+ #
57
+ # @return [Options::Base] a new options object
58
+ def options
59
+ @tool.options_class_for(self.class.command_name).new
60
+ end
61
+
62
+ # Get the command name
63
+ #
64
+ # @return [Symbol] the command name
65
+ def command_name
66
+ self.class.command_name
67
+ end
68
+
69
+ # Get the command definition
70
+ #
71
+ # @return [Hash] the command definition
72
+ def command_def
73
+ self.class.command_def
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ukiryu
4
+ # Bounded LRU cache with expiration support
5
+ #
6
+ # This cache provides:
7
+ # - Maximum size limit (evicts oldest entries when limit reached)
8
+ # - Time-to-live (TTL) expiration for entries
9
+ # - Thread-safe operations
10
+ #
11
+ # @example
12
+ # cache = Cache.new(max_size: 100, ttl: 300)
13
+ # cache[:tool] = tool_instance
14
+ # value = cache[:tool]
15
+ #
16
+ class Cache
17
+ # Cache entry with expiration tracking
18
+ #
19
+ # @api private
20
+ class Entry
21
+ attr_reader :value, :created_at, :accessed_at
22
+
23
+ def initialize(value)
24
+ @value = value
25
+ @created_at = Time.now
26
+ @accessed_at = @created_at
27
+ end
28
+
29
+ def touch!
30
+ @accessed_at = Time.now
31
+ end
32
+
33
+ def expired?(ttl)
34
+ return false unless ttl
35
+
36
+ (Time.now - @created_at) > ttl
37
+ end
38
+ end
39
+
40
+ # Initialize a new cache
41
+ #
42
+ # @param max_size [Integer] maximum number of entries (default: 100)
43
+ # @param ttl [Integer, nil] time-to-live in seconds (nil = no expiration)
44
+ # @option options [Boolean] :thread_safe enable thread-safe operations (default: true)
45
+ def initialize(max_size: 100, ttl: nil, thread_safe: true)
46
+ @max_size = max_size
47
+ @ttl = ttl
48
+ @thread_safe = thread_safe
49
+ @data = {}
50
+ @mutex = Mutex.new if thread_safe
51
+ end
52
+
53
+ # Get a value from the cache
54
+ #
55
+ # @param key [Object] the cache key
56
+ # @return [Object, nil] the cached value or nil if not found/expired
57
+ def [](key)
58
+ synchronize do
59
+ entry = @data[key]
60
+ return nil unless entry
61
+
62
+ # Check expiration
63
+ if entry.expired?(@ttl)
64
+ @data.delete(key)
65
+ return nil
66
+ end
67
+
68
+ # Update access time for LRU
69
+ entry.touch!
70
+ entry.value
71
+ end
72
+ end
73
+
74
+ # Set a value in the cache
75
+ #
76
+ # @param key [Object] the cache key
77
+ # @param value [Object] the value to cache
78
+ # @return [Object] the cached value
79
+ def []=(key, value)
80
+ synchronize do
81
+ # Evict oldest entry if at capacity
82
+ evict_if_needed
83
+
84
+ @data[key] = Entry.new(value)
85
+ value
86
+ end
87
+ end
88
+
89
+ # Check if a key exists in the cache (and is not expired)
90
+ #
91
+ # @param key [Object] the cache key
92
+ # @return [Boolean] true if key exists and is not expired
93
+ def key?(key)
94
+ synchronize do
95
+ entry = @data[key]
96
+ return false unless entry
97
+
98
+ if entry.expired?(@ttl)
99
+ @data.delete(key)
100
+ return false
101
+ end
102
+
103
+ true
104
+ end
105
+ end
106
+
107
+ # Delete a key from the cache
108
+ #
109
+ # @param key [Object] the cache key
110
+ # @return [Object, nil] the deleted value or nil if not found
111
+ def delete(key)
112
+ synchronize do
113
+ entry = @data.delete(key)
114
+ entry&.value
115
+ end
116
+ end
117
+
118
+ # Clear all entries from the cache
119
+ #
120
+ # @return [void]
121
+ def clear
122
+ synchronize do
123
+ @data.clear
124
+ end
125
+ end
126
+
127
+ # Get the current number of entries
128
+ #
129
+ # @return [Integer] number of entries
130
+ def size
131
+ @data.size
132
+ end
133
+
134
+ # Check if the cache is empty
135
+ #
136
+ # @return [Boolean] true if cache is empty
137
+ def empty?
138
+ @data.empty?
139
+ end
140
+
141
+ # Get all keys (excluding expired entries)
142
+ #
143
+ # @return [Array<Object>] array of keys
144
+ def keys
145
+ synchronize do
146
+ cleanup_expired
147
+ @data.keys
148
+ end
149
+ end
150
+
151
+ # Get cache statistics
152
+ #
153
+ # @return [Hash] statistics about the cache
154
+ def stats
155
+ synchronize do
156
+ cleanup_expired
157
+ {
158
+ size: @data.size,
159
+ max_size: @max_size,
160
+ ttl: @ttl,
161
+ utilization: @data.size.to_f / @max_size
162
+ }
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ # Synchronize block if thread-safe mode is enabled
169
+ #
170
+ # @api private
171
+ def synchronize(&block)
172
+ if @thread_safe
173
+ @mutex.synchronize(&block)
174
+ else
175
+ yield
176
+ end
177
+ end
178
+
179
+ # Evict oldest entries if cache is at capacity
180
+ #
181
+ # @api private
182
+ def evict_if_needed
183
+ return if @data.size < @max_size
184
+
185
+ # Find and remove the least recently used entry
186
+ lru_key = @data.min_by { |_, v| v.accessed_at }&.first
187
+ @data.delete(lru_key) if lru_key
188
+ end
189
+
190
+ # Remove expired entries
191
+ #
192
+ # @api private
193
+ def cleanup_expired
194
+ return unless @ttl
195
+
196
+ @data.delete_if { |_, entry| entry.expired?(@ttl) }
197
+ end
198
+ end
199
+ end