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
data/README.adoc CHANGED
@@ -1,866 +1,1891 @@
1
1
  = Ukiryu
2
2
 
3
- Platform-adaptive command execution framework using declarative YAML tool
4
- profiles.
3
+ image:https://img.shields.io/gem/v/ukiryu.svg[RubyGems Version]
4
+ image:https://img.shields.io/github/license/ukiryu/ukiryu.svg[License]
5
+ image:https://github.com/ukiryu/ukiryu/actions/workflows/test.yml/badge.svg["Build", link="https://github.com/ukiryu/ukiryu/actions/workflows/test.yml"]
6
+
7
+
8
+ == Ukiryu: Open Definitions For CLI Tools
9
+
10
+ The "OpenAPI" for Command Line Interfaces.
11
+
12
+ // Purpose
13
+ == Purpose
14
+
15
+ Ukiryu is a framework for defining command-line interfaces as declarative APIs.
16
+
17
+ It makes the following possible:
18
+
19
+ * Declarative definition of command-line interfaces, options, parameters and
20
+ typed arguments
21
+ * Harmonized definitions across platforms and shell environments
22
+ * Unified action interface across versioned and variant command interfaces
23
+ * Structured input and output handling, with type safety and validation
24
+
25
+
26
+ The Ukiryu framework has the following components:
27
+
28
+ * **Register**: A collection of tool profiles encoded in YAML, organized by tool
29
+ name and version
30
+
31
+ * **Schema**: A formal definition of the structure and types used in tool
32
+ profiles
33
+
34
+ * **Runtime**: A platform-adaptive Ruby library that provides a harmonized API
35
+ interface for CLI commands.
36
+
37
+
38
+ == Origin
39
+
40
+ The name 浮流 "ukiryu" (lit. "Floating Flow") is inspired by 天浮橋
41
+ "ame-no-ukihashi" (lit. "Floating Bridge of Heaven"), the mythical bridge that
42
+ connects heaven and earth.
43
+
44
+ The ame-no-ukihashi is described in Japanese mythology as the place where the
45
+ God Izanagi and Goddess Izanami stood at creating the Japanese archipelago.
46
+
47
+ The pronunciation of "ukiryu" ("yoo-kee-rhoo") is the English transliteration of
48
+ the Kanji characters 「浮流」, read in Hiragana as 「うきりゅう」.
49
+
50
+ In the view that a command line tool is a vessel for performing tasks, Ukiryu
51
+ serves as the flexible flow that guides its path through different environments.
52
+
53
+
54
+ == Why Ukiryu?
55
+
56
+ * **Platform-adaptive**: Commands work seamlessly across macOS, Linux, and Windows
57
+ * **Shell-aware**: Proper quoting and escaping for bash, zsh, fish, PowerShell, and cmd
58
+ * **Versioned**: Support multiple tool versions with distinct interfaces
59
+ * **Interface-capable**: Different implementations of the same command interface
60
+ * **Declarative**: Tool behavior defined in YAML profiles, not code
61
+ * **Type-safe**: Parameter validation with automatic type coercion
62
+
63
+ == Features
64
+
65
+ * **Schema-Driven**: Define CLI behavior in declarative YAML profiles
66
+ * **Type-Safe**: Automatic parameter validation and type coercion
67
+ * **Platform-Aware**: Automatic adaptation across macOS, Linux, and Windows
68
+ * **Structured Results**: Rich response objects instead of parsing stdout/stderr
69
+ * **Versioned APIs**: Support multiple tool versions with compatibility matrices
70
+ * **Intelligent Discovery**: Self-documenting commands with built-in introspection
71
+
72
+ === What Makes CLIs Unpredictable?
73
+
74
+ Without Ukiryu, CLI tools suffer from:
75
+
76
+ * **Argument Fragility**: Positional args break when order changes
77
+ * **Output Parsing Hell**: Grepping unstructured text is brittle
78
+ * **Platform Divergence**: Same command differs on macOS vs Linux
79
+ * **Version Drift**: Tools change behavior between versions
80
+ * **Error Ambiguity**: Is exit code 1 a timeout or validation error?
81
+ * **Discovery Friction**: Which flags exist? What's the syntax?
82
+
83
+ === The Ukiryu Solution
84
+
85
+ [cols="1,1,4"]
86
+ |===|===
87
+ |Problem |Traditional CLI |Ukiryu Solution
88
+
89
+ |Argument fragility
90
+ |`tool -o output -f input file.txt`
91
+ |`tool input=input.txt output=output.txt`
92
+
93
+ |Output parsing
94
+ |`grep "success" output.txt`
95
+ |`result.success?` (boolean)
96
+
97
+ |Platform divergence
98
+ |Different quoting per OS
99
+ |Same code works everywhere
100
+
101
+ |Version drift
102
+ |Breaking changes silently break scripts
103
+ |Version-specific YAML profiles
104
+
105
+ |Error ambiguity
106
+ |Exit code 1 = ???|TimeoutError, ValidationError, etc.
107
+
108
+ |Discovery friction
109
+ |`tool --help` |`ukiryu describe tool command`
110
+ |===
111
+
112
+ === Why "OpenAPI for CLIs"?
113
+
114
+ OpenAPI revolutionized REST APIs by:
115
+
116
+ 1. **Standardizing** - Machine-readable schema definitions
117
+ 2. **Documenting** - Auto-generated interactive documentation
118
+ 3. **Type-Safe** - Request/response validation
119
+ 4. **Versioning** - Multiple API versions coexisting
120
+
121
+ Ukiryu brings the same revolution to CLI tools:
122
+
123
+ * **Schema**: YAML profiles replace grepping --help
124
+ * **Docs**: `ukiryu describe` replaces man pages
125
+ * **Types**: Named parameters replace positional args
126
+ * **Versions**: `inkscape/1.0.yaml` and `inkscape/0.92.yaml` coexist
127
+
128
+ === Use Cases
129
+
130
+ * **CLI Developers**: Distribute "intelligent man pages" with your CLI
131
+ * **Tool Users**: Get deterministic, documented command behavior
132
+ * **CI/CD**: Run commands with structured error handling
133
+ * **Libraries**: Wrap CLI tools in type-safe Ruby interfaces
134
+ ***DevOps**: Manage tool versions and compatibility automatically
135
+
136
+ // What is Ukiryu?
137
+ == What is Ukiryu?
138
+
139
+ Ukiryu is a framework that turns CLI tools into well-defined, versioned APIs through declarative YAML profiles.
140
+
141
+ === Key Concepts
142
+
143
+ * **Tool**: A command-line utility (e.g., `inkscape`, `imagemagick`)
144
+ * **Command**: An action a tool performs (e.g., `export`, `convert`)
145
+ * **Action**: A specific operation within a command (e.g., `export_pdf`)
146
+ * **Routing**: Mapping command names to their implementations
147
+ * **Profile**: YAML file defining tool behavior across platforms/versions
148
+
149
+ === Architecture
150
+
151
+ [source]
152
+ ----
153
+ ┌─────────────────────────────────────────────────────────────────────┐
154
+ │ │
155
+ │ User Code (Ruby / CLI) │
156
+ │ ├─ tool.execute(:inkscape, { inputs: [...] }) │
157
+ │ └─ ukiryu exec inkscape export inputs=... │
158
+ │ │
159
+ └─────────────────────────────┬─────────────────────────────────────┘
160
+
161
+
162
+ ┌─────────────────────────────▼─────────────────────────────────────┐
163
+ │ Ukiryu Framework │
164
+ │ ├─ Register (loads YAML profiles) │
165
+ │ ├─ Tool (selects profile, detects version) │
166
+ │ ├─ CommandBuilder (formats arguments for shell) │
167
+ │ ├─ Executor (runs commands, captures output) │
168
+ │ └─ Shell Layer (platform-specific quoting/escaping) │
169
+ │ │
170
+ └─────────────────────────────┬─────────────────────────────────────┘
171
+
172
+
173
+ ┌─────────────────────────────▼─────────────────────────────────────┐
174
+ │ YAML Tool Profiles (Declarative API Definition) │
175
+ │ ├─ tools/inkscape/1.0.yaml (Modern Inkscape) │
176
+ │ ├─ tools/inkscape/0.92.yaml (Legacy Inkscape) │
177
+ │ └─ tools/imagemagick/7.1.yaml (ImageMagick 7.1) │
178
+ └──────────────────────────────────────────────────────────────────────┘
179
+ ----
180
+
181
+ === Tool Profile Schema
182
+
183
+ A tool profile is a YAML file that defines a CLI's complete API surface:
184
+
185
+ [source,yaml]
186
+ ----
187
+ # tools/inkscape/1.0.yaml
188
+ ---
189
+ ukiryu_schema: "1.2"
190
+
191
+ # Tool Metadata
192
+ name: inkscape
193
+ version: "1.0"
194
+ $self: https://www.ukiryu.com/register/1.0/inkscape/1.0
195
+
196
+ # Version Detection
197
+ version_detection:
198
+ command: "--version"
199
+ pattern: "Inkscape (\\d+\\.\\d+)"
200
+ modern_threshold: "1.0"
201
+
202
+ # Search Paths (platform-specific)
203
+ search_paths:
204
+ macos:
205
+ - "/Applications/Inkscape.app/Contents/MacOS/inkscape"
206
+ linux:
207
+ - "/usr/bin/inkscape"
208
+ windows:
209
+ - "C:/Program Files/Inkscape/bin/inkscape.exe"
210
+
211
+ # Platform/Shell/Version Profiles
212
+ profiles:
213
+ - name: modern_unix
214
+ platforms: [macos, linux]
215
+ shells: [bash, zsh, fish, sh]
216
+ version: ">= 1.0"
217
+ option_style: double_dash_equals
218
+
219
+ # Commands (API Endpoints)
220
+ commands:
221
+ - name: export
222
+ description: Export document to different format
223
+
224
+ # Environment variables to apply for this command
225
+ use_env_vars: [headless]
226
+
227
+ # Request Parameters
228
+ arguments:
229
+ - name: inputs
230
+ type: file
231
+ variadic: true
232
+ position: last
233
+ min: 1
234
+ description: Input file(s)
235
+
236
+ options:
237
+ - name: output
238
+ type: file
239
+ cli: --export-filename
240
+ format: double_dash_equals
241
+ required: true
242
+ description: Output filename
243
+
244
+ - name: format
245
+ type: symbol
246
+ cli: --export-type
247
+ format: double_dash_equals
248
+ values: [svg, png, pdf, eps, ps]
249
+ description: Output format
250
+
251
+ flags:
252
+ - name: batch_process
253
+ cli: --batch-process
254
+ position_constraint: prefix
255
+ default: true
256
+ description: Close GUI after processing
257
+
258
+ # Exit Codes (API Response Status)
259
+ exit_codes:
260
+ standard:
261
+ "0": "Success"
262
+ "1": "File not found or cannot be opened"
263
+ "3": "Initialization error"
264
+ custom:
265
+ "2": "Export failed (see stderr for details)"
266
+
267
+ # Response Format
268
+ parse_output:
269
+ type: hash
270
+ pattern: 'key:\\s*value'
271
+ ----
272
+
273
+ // Tool, Command, Action, and Routing
274
+ == Tool, Command, Action, and Routing
275
+
276
+ Ukiryu organizes CLI functionality into a hierarchical structure similar to REST API resources.
277
+
278
+ === Tool
279
+
280
+ A **tool** represents a command-line utility (e.g., `inkscape`, `imagemagick`).
281
+
282
+ [source,ruby]
283
+ ----
284
+ # Get a tool by name
285
+ tool = Ukiryu::Tool.get(:inkscape)
286
+ ----
287
+
288
+ *Has metadata*: name, version, homepage, aliases
289
+ *Detects version**: Uses `version_detection` from profile
290
+ *Finds executable**: Searches `search_paths` for the tool
291
+ *Loads profile**: Selects appropriate YAML profile based on version
292
+
293
+ === Command
294
+
295
+ A **command** is a major operation a tool performs (e.g., `export`, `convert`, `query`).
296
+
297
+ [source,yaml]
298
+ ----
299
+ commands:
300
+ - name: export
301
+ description: Export document to different format
302
+ usage: inkscape [OPTIONS] input1.svg [input2.svg ...]
303
+ ----
304
+
305
+ [source,ruby]
306
+ ----
307
+ # Execute a command
308
+ result = tool.execute(:export, { inputs: ['drawing.svg'], output: 'drawing.png' })
309
+ ----
310
+
311
+ *Has parameters*: arguments, options, flags
312
+ *Returns result*: Structured response object
313
+ *May have execution mode*: e.g., `headless` for GUI tools
314
+
315
+ === Action
316
+
317
+ An **action** is a specific operation within a command, used for complex CLIs with nested commands.
318
+
319
+ [source,yaml]
320
+ ----
321
+ # Git has nested commands
322
+ commands:
323
+ - name: remote
324
+ description: Manage set of tracked repositories
325
+
326
+ - name: branch
327
+ description: Manage, create, delete, list branch names
328
+ ----
329
+
330
+ With routing:
331
+
332
+ [source,yaml]
333
+ ----
334
+ routing:
335
+ remote: git-remote # Maps "tool remote" to git-remote command
336
+ branch: git-branch # Maps "tool branch" to git-branch command
337
+ ----
338
+
339
+ [source,ruby]
340
+ ----
341
+ # Actions are executed via the command name
342
+ git_tool = Ukiryu::Tool.get(:git)
343
+
344
+ # Execute 'git remote add' action
345
+ git_tool.execute(:remote, :add, {
346
+ name: 'origin',
347
+ url: 'https://github.com/user/repo.git'
348
+ })
349
+
350
+ # Execute 'git branch delete' action
351
+ git_tool.execute(:branch, :delete, {
352
+ branch: 'feature-branch',
353
+ force: true
354
+ })
355
+ ----
356
+
357
+ === Routing
358
+
359
+ **Routing** maps command names to their implementations, enabling:
360
+
361
+ 1. **Tool aliases**: Multiple tools implementing the same interface
362
+ 2. **Version routing**: Different versions for different behaviors
363
+ 3. **Multi-level hierarchies**: Parent/child command relationships
364
+
365
+ [source,yaml]
366
+ ----
367
+ # Ping has platform-specific implementations
368
+ tools:
369
+ ping: # Abstract interface
370
+ implements: ping
371
+
372
+ ping_bsd: # BSD ping implementation
373
+ implements: ping
374
+ platforms: [freebsd, openbsd, netbsd]
375
+
376
+ ping_gnu: # GNU ping implementation
377
+ implements: ping
378
+ platforms: [linux]
379
+ ----
380
+
381
+ [source,ruby]
382
+ ----
383
+ # Ukiryu automatically routes to correct implementation
384
+ tool = Ukiryu::Tool.get(:ping)
385
+ # On FreeBSD: uses ping_bsd
386
+ # On Linux: uses ping_gnu
387
+ ----
388
+
389
+ === Default Command Resolution
390
+
391
+ When a tool's name matches its command name, you can omit the command:
392
+
393
+ [source,ruby]
394
+ ----
395
+ # Both syntaxes work when tool.name == command.name
396
+ tool.execute(:ping, { host: '127.0.1.1', count: 1 })
397
+ tool.execute(:ping, :ping, { host: '127.0.1.1', count: 1 })
398
+ ----
399
+
400
+ Similarly in CLI:
401
+
402
+ [source,bash]
403
+ ----
404
+ ukiryu exec ping host=127.0.1.1 count=1
405
+ ukiryu exec ping ping host=127.0.1.1 count=1
406
+ ----
407
+
408
+ === Complete Example: Inkscape Export
409
+
410
+ Here's a complete example showing all concepts:
411
+
412
+ [source,ruby]
413
+ ----
414
+ require 'ukiryu'
415
+
416
+ # 1. Get the tool
417
+ tool = Ukiryu::Tool.get(:inkscape)
418
+
419
+ # 2. Check availability
420
+ unless tool.available?
421
+ puts "Inkscape not installed"
422
+ exit 1
423
+ end
424
+
425
+ # 3. Execute the export command
426
+ result = tool.execute(:export, {
427
+ inputs: ['~/drawing.svg'],
428
+ output: '~/drawing.png',
429
+ format: :png,
430
+ dpi: 300,
431
+ width: 1024,
432
+ height: 768
433
+ })
434
+
435
+ # 4. Handle the result
436
+ if result.success?
437
+ puts "✓ Export successful"
438
+ puts " Duration: #{result.metadata.formatted_duration}"
439
+ puts " Command: #{result.command_info.full_command}"
440
+ else
441
+ puts "✗ Export failed"
442
+ puts " Exit code: #{result.output.exit_status}"
443
+ puts " Error: #{result.output.stderr}"
444
+ end
445
+ ----
446
+
447
+ Generated command:
448
+ [source]
449
+ ----
450
+ inkscape --batch-process --export-filename=/Users/user/drawing.png --export-type=png --export-width=1024 --dpi=300 /Users/user/drawing.svg
451
+ ----
452
+
453
+ // Configuration Methods
454
+ == Configuration Methods
455
+
456
+ Ukiryu supports three complementary configuration methods with clear precedence:
457
+
458
+ [NOTE]
459
+ .Configuration precedence (highest to lowest):
460
+ 1. Environment variables (`UKIRYU_*`)
461
+ 2. CLI parameters
462
+ 3. Ruby API parameters
463
+ 4. Profile defaults
464
+ ===
465
+
466
+ === Method 1: Environment Variables (Highest Precedence)
467
+
468
+ [source,bash]
469
+ ----
470
+ # Global configuration
471
+ export UKIRYU_REGISTER=/path/to/register
472
+ export UKIRYU_TIMEOUT=180
473
+ export UKIRYU_DEBUG=true
474
+
475
+ # All commands use these settings
476
+ ukiryu exec inkscape export inputs=drawing.svg output=drawing.png
477
+ ----
478
+
479
+ === Method 2: CLI Parameters (Medium Precedence)
480
+
481
+ [source,bash]
482
+ ----
483
+ # Per-command configuration
484
+ ukiryu exec inkscape export \
485
+ inputs=drawing.svg \
486
+ output=drawing.png \
487
+ timeout=120
488
+ ----
489
+
490
+ === Method 3: Ruby API (Lowest Precedence)
491
+
492
+ [source,ruby]
493
+ ----
494
+ # Programmatic configuration
495
+ Ukiryu::Register.default_register_path = '/path/to/register'
496
+ Ukiryu::Config.debug = true
497
+
498
+ tool = Ukiryu::Tool.get(:inkscape)
499
+ result = tool.execute(:export, params, timeout: 60)
500
+ ----
501
+
502
+ === Precedence Example
503
+
504
+ [source,ruby]
505
+ ----
506
+ # Profile default: 90 seconds
507
+ # Ruby API parameter: 60 seconds
508
+ # CLI parameter: 120 seconds
509
+ # ENV variable: 180 seconds (WINS!)
510
+
511
+ ENV['UKIRYU_TIMEOUT'] = '180'
512
+ tool = Ukiryu::Tool.get(:inkscape)
513
+
514
+ result = tool.execute(:export, {
515
+ inputs: ['drawing.svg'],
516
+ output: 'drawing.png'
517
+ }, timeout: 60)
518
+
519
+ # Result: Uses 180 second timeout from ENV
520
+ ----
521
+
522
+ // Installation
523
+ == Installation
524
+
525
+ Add this line to your application's Gemfile:
526
+
527
+ [source,ruby]
528
+ ----
529
+ gem 'ukiryu'
530
+ ----
531
+
532
+ And then execute:
533
+
534
+ [source,shell]
535
+ ----
536
+ bundle install
537
+ ----
538
+
539
+ Or install it yourself as:
540
+
541
+ [source,shell]
542
+ ----
543
+ gem install ukiryu
544
+ ----
545
+
546
+ // Quick Start
547
+ == Quick Start
548
+
549
+ === For CLI Tool Users
550
+
551
+ Make your CLI "intelligent" with a bundled Ukiryu definition:
552
+
553
+ [source,bash]
554
+ ----
555
+ mytool --definition > mytool-1.0.yaml
556
+ mytool --validate-definition mytool-1.0.yaml
557
+ ----
558
+
559
+ Your tool now has:
560
+ * Auto-generated `ukiryu describe mytool` documentation
561
+ * Type-safe parameter validation
562
+ * Platform-aware command formatting
563
+ * Structured error responses
564
+
565
+ === For Ruby Developers
566
+
567
+ [source,ruby]
568
+ ----
569
+ require 'ukiryu'
570
+
571
+ # Configure register
572
+ Ukiryu::Register.default_register_path = 'path/to/register'
573
+
574
+ # Get a tool
575
+ tool = Ukiryu::Tool.get(:inkscape)
576
+
577
+ # Execute with type safety
578
+ result = tool.execute(:export, {
579
+ inputs: ['drawing.svg'],
580
+ output: 'drawing.png',
581
+ format: :png, # Symbol - validated against values list
582
+ dpi: 300, # Integer - range checked
583
+ width: 1024 # Integer - range checked
584
+ })
585
+
586
+ puts result.success? ? "Success!" : "Failed: #{result.stderr}"
587
+ ----
588
+
589
+ === For DevOps Engineers
590
+
591
+ [source,yaml]
592
+ ----
593
+ # .github/workflows/convert.yml
594
+ name: Convert Images
595
+
596
+ on: [push]
597
+
598
+ jobs:
599
+ convert:
600
+ runs-on: ubuntu-latest
601
+ steps:
602
+ - uses: actions/checkout@v4
603
+
604
+ - name: Install Ukiryu
605
+ run: gem install ukiryu
606
+
607
+ - name: Convert SVG to PNG
608
+ env:
609
+ UKIRYU_REGISTER: ./register
610
+ run: |
611
+ ukiryu exec inkscape export \
612
+ inputs=drawing.svg \
613
+ output=drawing.png \
614
+ format=png
615
+ ----
616
+
617
+ // Distributed Definitions
618
+ == Distributed Definitions
619
+
620
+ Ukiryu supports loading tool definitions from external files, enabling tool authors to distribute definitions independently from the central register. This allows tools to be distributed with their own definitions, versioned alongside the tool itself.
621
+
622
+ === Loading Definitions from Files
623
+
624
+ Load a tool definition from a local file:
625
+
626
+ [source,ruby]
627
+ ----
628
+ require 'ukiryu'
629
+
630
+ # Load from file path
631
+ tool = Ukiryu::Tool.load('path/to/mytool.yaml')
632
+
633
+ # Execute using the loaded definition
634
+ result = tool.execute(:command, { param: 'value' })
635
+ ----
636
+
637
+ The `Tool.load` method (also available as `Tool.from_file`) loads a YAML definition from the filesystem:
638
+
639
+ * Validates the YAML structure
640
+ * Checks required fields (name, version, profiles)
641
+ * Caches the definition based on file path and modification time
642
+ * Tracks file metadata for cache invalidation
643
+
644
+ === Loading Definitions from Strings
645
+
646
+ Load a tool definition from a YAML string:
647
+
648
+ [source,ruby]
649
+ ----
650
+ require 'ukiryu'
651
+
652
+ yaml_string = <<~YAML
653
+ name: mytool
654
+ version: "1.0"
655
+ profiles:
656
+ - name: default
657
+ platforms: [linux, macos]
658
+ shells: [bash, zsh]
659
+ commands:
660
+ - name: process
661
+ description: Process files
662
+ YAML
663
+
664
+ # Load from string
665
+ tool = Ukiryu::Tool.load_from_string(yaml_string)
666
+
667
+ # Execute using the loaded definition
668
+ result = tool.execute(:process, { inputs: ['file.txt'] })
669
+ ----
670
+
671
+ The `Tool.load_from_string` method (also available as `Tool.from_definition`) loads a definition from a YAML string.
672
+
673
+ === CLI Usage
674
+
675
+ Use the `--definition` (or `-d`) flag to load a definition from a file:
676
+
677
+ [source,bash]
678
+ ----
679
+ # Execute using a local definition file
680
+ ukiryu exec --definition ./mytool.yaml mytool command param=value
681
+
682
+ # Short form
683
+ ukiryu exec -d ./mytool.yaml mytool command param=value
684
+ ----
685
+
686
+ === Definition Source Tracking
687
+
688
+ Tools loaded from definitions track their source:
689
+
690
+ [source,ruby]
691
+ ----
692
+ tool = Ukiryu::Tool.load('path/to/tool.yaml')
693
+
694
+ # Get information about the definition source
695
+ tool.definition_source # => #<Ukiryu::Definition::Sources::FileSource>
696
+ tool.definition_path # => "/absolute/path/to/tool.yaml"
697
+ tool.definition_mtime # => 2024-01-15 10:30:00 +0800
698
+ ----
699
+
700
+ For string-based definitions:
701
+
702
+ [source,ruby]
703
+ ----
704
+ tool = Ukiryu::Tool.load_from_string(yaml_string)
705
+
706
+ # String sources have content hash instead of path/mtime
707
+ tool.definition_source # => #<Ukiryu::Definition::Sources::StringSource>
708
+ tool.definition_path # => nil (not applicable for string sources)
709
+ tool.definition_source.content_hash # => "a1b2c3d4..."
710
+ ----
711
+
712
+ === Validation Modes
713
+
714
+ Definition loading supports three validation modes:
715
+
716
+ [source,ruby]
717
+ ----
718
+ # Strict mode (default) - raises errors for invalid definitions
719
+ tool = Ukiryu::Tool.load('tool.yaml', validation: :strict)
720
+
721
+ # Lenient mode - warns but continues
722
+ tool = Ukiryu::Tool.load('tool.yaml', validation: :lenient)
723
+
724
+ # None mode - skips validation entirely
725
+ tool = Ukiryu::Tool.load('tool.yaml', validation: :none)
726
+ ----
727
+
728
+ * **:strict** (default) - Raises `DefinitionValidationError` for missing required fields
729
+ * **:lenient** - Prints warnings but continues loading
730
+ * **:none** - Skips all validation (useful for testing)
731
+
732
+ === Error Handling
733
+
734
+ The definition loading system provides specific error types:
735
+
736
+ [source,ruby]
737
+ ----
738
+ begin
739
+ tool = Ukiryu::Tool.load('tool.yaml')
740
+ rescue Ukiryu::DefinitionNotFoundError => e
741
+ # File does not exist
742
+ puts "Definition file not found: #{e.message}"
743
+ rescue Ukiryu::DefinitionLoadError => e
744
+ # Invalid YAML or file read error
745
+ puts "Failed to load definition: #{e.message}"
746
+ rescue Ukiryu::DefinitionValidationError => e
747
+ # Valid YAML but fails validation rules
748
+ puts "Definition validation failed: #{e.message}"
749
+ end
750
+ ----
751
+
752
+ === Definition Caching
753
+
754
+ Definitions are cached automatically based on:
755
+
756
+ * **FileSource**: File path SHA256 hash + modification time
757
+ * **StringSource**: Content SHA256 hash
758
+
759
+ Clear the cache:
760
+
761
+ [source,ruby]
762
+ ----
763
+ # Clear all cached definitions
764
+ Ukiryu::Tool.clear_definition_cache
765
+
766
+ # Clear specific source (if you have the source object)
767
+ source = Ukiryu::Definition::Sources::FileSource.new('tool.yaml')
768
+ Ukiryu::Definition::Loader.clear_cache(source)
769
+ ----
770
+
771
+ === Definition Source Classes
772
+
773
+ The definition loading system uses an abstract source pattern:
774
+
775
+ * **Source** - Abstract base class for all definition sources
776
+ * **FileSource** - Loads from filesystem paths with modification time tracking
777
+ * **StringSource** - Loads from YAML strings with content hashing
778
+ * **BundledSource** - (Planned) Loads from tool-bundled locations
779
+ * **RegisterSource** - (Planned) Loads from central register
780
+
781
+ Each source provides:
782
+ * `load` - Returns the YAML content
783
+ * `cache_key` - Unique identifier for caching
784
+ * `source_type` - Symbol identifying the source type
785
+
786
+ === Example: Bundling Definitions with Tools
787
+
788
+ Tool authors can bundle definitions with their tools:
789
+
790
+ [source,bash]
791
+ ----
792
+ # Tool installation directory structure
793
+ /opt/mytool/
794
+ ├── bin/mytool
795
+ ├── share/ukiryu/
796
+ │ └── 1.0.yaml # Tool definition
797
+ └── lib/
798
+ ----
799
+
800
+ Users can then load the bundled definition:
801
+
802
+ [source,ruby]
803
+ ----
804
+ # Load from bundled path
805
+ tool = Ukiryu::Tool.load('/opt/mytool/share/ukiryu/1.0.yaml')
806
+ ----
807
+
808
+ === Automatic Definition Discovery
809
+
810
+ Ukiryu can automatically discover tool definitions in standard filesystem locations following the XDG Base Directory Specification. This allows tools and users to install definitions in well-known locations without manual configuration.
811
+
812
+ ==== Discovery Priority
813
+
814
+ Definitions are searched in the following priority order (highest to lowest):
815
+
816
+ 1. **User definitions** - `~/.local/share/ukiryu/definitions/`
817
+ 2. **Tool-bundled paths** - Auto-discovered from PATH
818
+ 3. **Local system** - `/usr/local/share/ukiryu/definitions/`
819
+ 4. **System** - `/usr/share/ukiryu/definitions/`
820
+
821
+ When multiple definitions exist for the same tool, the highest priority definition is used.
822
+
823
+ ==== XDG Compliance
824
+
825
+ Ukiryu respects XDG environment variables:
826
+
827
+ [source,bash]
828
+ ----
829
+ # Override user data directory
830
+ export XDG_DATA_HOME=/custom/data
831
+
832
+ # Override system data directories
833
+ export XDG_DATA_DIRS=/path1:/path2
834
+ ----
835
+
836
+ Default values:
837
+ * `XDG_DATA_HOME`: `~/.local/share` (if not set)
838
+ * `XDG_DATA_DIRS`: `/usr/local/share:/usr/share` (if not set)
839
+
840
+ ==== Tool-Bundled Definition Detection
841
+
842
+ When tools are installed, Ukiryu automatically detects bundled definitions:
843
+
844
+ [source,bash]
845
+ ----
846
+ # Standard installation structure
847
+ /usr/local/
848
+ ├── bin/
849
+ │ └── mytool # Tool executable
850
+ ├── share/
851
+ │ └── ukiryu/
852
+ │ └── 1.0.yaml # Tool definition (auto-discovered)
853
+ └── lib/
854
+ ----
855
+
856
+ Ukiryu also detects definitions in `/opt` installations:
857
+
858
+ [source,bash]
859
+ ----
860
+ /opt/mytool/
861
+ ├── bin/mytool
862
+ ├── ukiryu/
863
+ │ └── 1.0.yaml # Auto-discovered from /opt
864
+ └── lib/
865
+ ----
866
+
867
+ ==== Directory Structure
868
+
869
+ Definitions follow a structured directory layout:
870
+
871
+ [source,bash]
872
+ ----
873
+ ~/.local/share/ukiryu/definitions/
874
+ ├── inkscape/
875
+ │ ├── 1.0.yaml
876
+ │ └── 0.92.yaml
877
+ ├── imagemagick/
878
+ │ ├── 7.1.yaml
879
+ │ └── 6.0.yaml
880
+ └── mytool/
881
+ └── 1.5.yaml
882
+ ----
883
+
884
+ Each tool has its own subdirectory, with version-specific YAML files.
885
+
886
+ ==== Finding Definitions Programmatically
887
+
888
+ [source,ruby]
889
+ ----
890
+ require 'ukiryu'
5
891
 
6
- image:https://img.shields.io/gem/v/ukiryu.svg[RubyGems Version]
7
- image:https://img.shields.io/github/license/ukiryu/ukiryu.svg[License]
8
- image:https://github.com/ukiryu/ukiryu/actions/workflows/test.yml/badge.svg["Build", link="https://github.com/ukiryu/ukiryu/actions/workflows/test.yml"]
892
+ # Discover all available definitions
893
+ definitions = Ukiryu::Definition::Discovery.discover
9
894
 
10
- == Purpose
895
+ # Get definitions for a specific tool
896
+ inkscape_defs = Ukiryu::Definition::Discovery.definitions_for('inkscape')
11
897
 
12
- Ukiryu provides a Ruby framework for executing external commands with:
898
+ # Find the best definition (highest priority, latest version)
899
+ metadata = Ukiryu::Definition::Discovery.find('inkscape')
13
900
 
14
- * Declarative YAML tool profiles
15
- * Platform-specific command formatting (macOS, Linux, Windows)
16
- * Shell-specific syntax (bash, zsh, fish, PowerShell, cmd)
17
- * Version-aware tool detection
18
- * Fully object-oriented result handling
901
+ # Find a specific version
902
+ metadata = Ukiryu::Definition::Discovery.find('inkscape', '1.0')
19
903
 
20
- == Features
904
+ # List all available tool names
905
+ tools = Ukiryu::Definition::Discovery.available_tools
906
+ # => ["inkscape", "imagemagick", "mytool"]
907
+ ----
21
908
 
22
- === Default Command Resolution
909
+ ==== Definition Metadata
23
910
 
24
- When a tool implements its own name (e.g., `ping` tool implements `ping` command), you can omit the command name:
911
+ Each discovered definition has associated metadata:
25
912
 
26
913
  [source,ruby]
27
914
  ----
28
- # Both syntaxes work
29
- tool.execute(:ping, { host: '127.0.0.1', count: 1 })
30
- tool.execute(:ping, :ping, { host: '127.0.0.1', count: 1 })
31
- ----
915
+ metadata = Ukiryu::Definition::Discovery.find('inkscape')
32
916
 
33
- The CLI also supports this shorthand:
917
+ # Basic information
918
+ metadata.name # => "inkscape"
919
+ metadata.version # => "1.0"
920
+ metadata.path # => "/home/user/.local/share/ukiryu/definitions/inkscape/1.0.yaml"
921
+ metadata.source_type # => :user (or :bundled, :local_system, :system)
34
922
 
35
- [source,shell]
923
+ # File information
924
+ metadata.exists? # => true
925
+ metadata.mtime # => 2024-01-15 10:30:00 +0800
926
+
927
+ # Load the full definition
928
+ definition = metadata.load_definition
36
929
  ----
37
- # Both syntaxes work
38
- ukiryu exec-inline ping host=127.0.0.1 count=1
39
- ukiryu exec-inline ping ping host=127.0.0.1 count=1
40
- ===
41
930
 
42
- === Configuration Management
931
+ ==== Source Type Priority
43
932
 
44
- Centralized configuration system supporting multiple sources:
933
+ Source types determine definition priority:
45
934
 
46
935
  [source,ruby]
47
936
  ----
48
- # Programmatic configuration
49
- Ukiryu::Config.configure do |config|
50
- config.format = :json
51
- config.debug = true
52
- config.timeout = 60
53
- config.registry = '/path/to/register'
54
- end
55
-
56
- # Or via environment variables
57
- ENV['UKIRYU_FORMAT'] = 'json'
58
- ENV['UKIRYU_DEBUG'] = '1'
59
- ENV['UKIRYU_TIMEOUT'] = '60'
937
+ # Priority values (lower = higher priority)
938
+ :user => 1 # User definitions (~/.local/share)
939
+ :bundled => 2 # Tool-bundled paths
940
+ :local_system => 3 # /usr/local/share
941
+ :system => 4 # /usr/share
942
+ :register => 5 # Central register (planned)
60
943
  ----
61
944
 
62
- Configuration priority (highest to lowest):
63
- . CLI options (`--format`, `--registry`, etc.)
64
- . Environment variables (`UKIRYU_*`)
65
- . Programmatic configuration (`Config.configure`)
66
- . Default values
945
+ When sorting definitions, higher priority comes first, then higher versions.
67
946
 
68
- === Debug Mode
947
+ ==== Version Sorting
69
948
 
70
- Comprehensive debug output for troubleshooting:
949
+ Definitions are sorted by version within each priority level:
71
950
 
72
- [source,shell]
951
+ [source,ruby]
952
+ ----
953
+ # For the same tool in the same location:
954
+ # 2.0.yaml comes before 1.0.yaml
955
+ # 1.10.yaml comes before 1.9.yaml (semantic versioning)
73
956
  ----
74
- # Enable debug mode
75
- UKIRYU_DEBUG=1 ukiryu exec-inline ping host=127.0.0.1 count=1
76
957
 
77
- # Debug sections output to stderr:
78
- # - Ukiryu CLI Options
79
- # - Tool Resolution Process
80
- # - Structured Options (tool command options)
81
- # - Shell Command (actual command to execute)
82
- # - Raw Command Response (stdout/stderr)
83
- # - Structured Response (final result)
958
+ ==== CLI Commands for Definitions
959
+
960
+ Ukiryu provides CLI commands for managing definitions:
961
+
962
+ [source,bash]
84
963
  ----
964
+ # List all discovered definitions
965
+ ukiryu definitions list
85
966
 
86
- Debug output uses structured borders with color support (when Paint gem is available).
967
+ # Show where definitions are searched
968
+ ukiryu definitions paths
87
969
 
88
- === Stdin Support
970
+ # Get information about a specific definition
971
+ ukiryu definitions info inkscape
89
972
 
90
- Ukiryu supports passing data to commands via standard input (stdin), enabling Unix pipe composition and integration with other tools.
973
+ # Install a definition to user directory
974
+ ukiryu definitions add ./mytool-1.0.yaml
91
975
 
92
- [source,shell]
976
+ # Remove a user definition
977
+ ukiryu definitions remove mytool
93
978
  ----
94
- # Read from pipe using --stdin flag
95
- echo '{"name": "value"}' | ukiryu exec jq --stdin filter="."
96
979
 
97
- # Read from pipe using stdin=- parameter
98
- echo '{"name": "value"}' | ukiryu exec jq stdin=- filter="."
980
+ ===== List Definitions
99
981
 
100
- # Read from file using stdin=@filename syntax
101
- ukiryu exec jq stdin=@/path/to/data.json filter="."
982
+ [source,bash]
983
+ ----
984
+ $ ukiryu definitions list
102
985
 
103
- # Pass data directly via stdin parameter
104
- ukiryu exec jq stdin='{"data": "value"}' filter="."
986
+ Available Definitions:
987
+ inkscape/1.0 (user) - ~/.local/share/ukiryu/definitions/inkscape/1.0.yaml
988
+ inkscape/0.92 (system) - /usr/share/ukiryu/definitions/inkscape/0.92.yaml
989
+ imagemagick/7.1 (bundled) - /usr/local/share/ukiryu/definitions/imagemagick/7.1.yaml
105
990
  ----
106
991
 
107
- The `stdin` parameter is a special execution parameter that is not passed to the tool's command arguments but is instead written to the child process's stdin stream. This enables composition with other Unix tools:
992
+ ===== Show Paths
108
993
 
109
- [source,shell]
994
+ [source,bash]
110
995
  ----
111
- # Compose with other tools
112
- cat large_data.json | ukiryu exec jq --stdin filter=".name" | sort
996
+ $ ukiryu definitions paths
113
997
 
114
- # Process output from another command
115
- curl -s https://api.example.com/data | ukiryu exec jq --stdin filter=".[0:5]"
998
+ Definition Search Paths:
999
+ 1. ~/.local/share/ukiryu/definitions (user)
1000
+ 2. /usr/local/share/ukiryu/definitions (local_system)
1001
+ 3. /usr/share/ukiryu/definitions (system)
116
1002
  ----
117
1003
 
118
- In dry-run mode, stdin data is previewed (first 100 characters) to show what would be passed to the command.
119
-
120
- === OOP API
1004
+ ===== Definition Info
121
1005
 
122
- Tool-specific classes with a fully object-oriented API:
1006
+ [source,bash]
1007
+ ----
1008
+ $ ukiryu definitions info inkscape
123
1009
 
124
- [source,ruby]
1010
+ Tool: inkscape
1011
+ Version: 1.0
1012
+ Source: user
1013
+ Path: ~/.local/share/ukiryu/definitions/inkscape/1.0.yaml
1014
+ File exists: Yes
1015
+ Modified: 2024-01-15 10:30:00 +0800
125
1016
  ----
126
- Ukiryu::Tools::Imagemagick.new.tap do |im|
127
- convert_options = im.options_for(:convert)
128
- convert_options.set(inputs: ["image.png"], resize: "50%")
129
- convert_options.output = "output.jpg"
130
- convert_options.run
131
- end
1017
+
1018
+ ===== Add Definition
1019
+
1020
+ [source,bash]
132
1021
  ----
1022
+ $ ukiryu definitions add ./mytool-1.0.yaml
133
1023
 
134
- See <<oop-api,OOP API>> for more details.
1024
+ Installing definition to user directory:
1025
+ Source: ./mytool-1.0.yaml
1026
+ Target: ~/.local/share/ukiryu/definitions/mytool/1.0.yaml
135
1027
 
136
- === Tool Registry
1028
+ Definition installed successfully
1029
+ ----
137
1030
 
138
- Load tool definitions from YAML profiles:
1031
+ ===== Remove Definition
139
1032
 
140
- [source,ruby]
1033
+ [source,bash]
141
1034
  ----
142
- Ukiryu::Registry.default_registry_path = 'path/to/register'
143
- tool = Ukiryu::Tool.get(:imagemagick)
1035
+ $ ukiryu definitions remove mytool
1036
+
1037
+ Removing user definition:
1038
+ Path: ~/.local/share/ukiryu/definitions/mytool/1.0.yaml
1039
+
1040
+ ✓ Definition removed successfully
144
1041
  ----
145
1042
 
146
- === Platform Detection
1043
+ // CLI Reference
1044
+ == CLI Reference
147
1045
 
148
- Automatic platform and shell detection:
1046
+ === List Available Tools
149
1047
 
150
- [source,ruby]
1048
+ [source,bash]
151
1049
  ----
152
- Ukiryu::Platform.detect # => :macos, :linux, or :windows
153
- Ukiryu::Shell.detect # => :bash, :zsh, :powershell, etc.
1050
+ ukiryu list
154
1051
  ----
155
1052
 
156
- === Version Awareness
1053
+ === Get Tool Information
157
1054
 
158
- Handle different tool versions with specific profiles:
159
-
160
- [source,ruby]
1055
+ [source,bash]
161
1056
  ----
162
- tool = Ukiryu::Tool.get(:inkscape)
163
- # Automatically selects 1.0.yaml or 0.92.yaml based on detected version
1057
+ ukiryu info inkscape
164
1058
  ----
165
1059
 
166
- === OOP Result Handling
1060
+ Shows:
1061
+ * Tool metadata (name, version, homepage)
1062
+ * Version detection method
1063
+ * Search paths for your platform
1064
+ * Available profiles with execution mode
167
1065
 
168
- Rich, object-oriented execution results:
1066
+ === Describe Commands
169
1067
 
170
- [source,ruby]
1068
+ [source,bash]
171
1069
  ----
172
- result = tool.execute(:convert, { inputs: ['input.png'], output: 'output.jpg' })
173
-
174
- result.command_info.executable # => "/opt/homebrew/bin/magick"
175
- result.output.stdout # => ""
176
- result.output.success? # => true
177
- result.metadata.duration # => 0.5
178
- result.metadata.formatted_duration # => "500ms"
1070
+ ukiryu describe inkscape export
179
1071
  ----
180
1072
 
181
- == Architecture
1073
+ Shows:
1074
+ * Command description and usage
1075
+ * All arguments with types and constraints
1076
+ * All options with types and formats
1077
+ * All flags with defaults
1078
+ * Exit codes and their meanings
182
1079
 
183
- === Class Hierarchy
1080
+ === Execute Commands
184
1081
 
185
- [source]
1082
+ [source,bash]
1083
+ ----
1084
+ ukiryu exec inkscape export \
1085
+ inputs=drawing.svg \
1086
+ output=drawing.png \
1087
+ format=png
186
1088
  ----
187
- Ukiryu
188
- ├── Executor
189
- │ ├── CommandInfo (command details)
190
- │ ├── Output (stdout/stderr with utilities)
191
- │ ├── ExecutionMetadata(timing information)
192
- │ └── Result (composes above three)
193
- ├── Tool (YAML profile loader and executor)
194
- ├── Registry (profile discovery and loading)
195
- ├── Platform (platform detection and utilities)
196
- ├── Shell
197
- │ ├── Base (abstract shell interface)
198
- │ ├── Bash
199
- │ ├── Zsh
200
- │ ├── Fish
201
- │ ├── Sh
202
- │ ├── PowerShell
203
- │ └── Cmd
204
- └── Errors (custom exception hierarchy)
205
- ----
206
-
207
- === Data Flow
208
1089
 
209
- [source]
1090
+ === System Information
1091
+
1092
+ [source,bash]
210
1093
  ----
211
- ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐
212
- │ YAML Profile │─────►│ Tool Class │─────►│ Executor │
213
- │ (declarative) │ │ (loader & │ │ (platform & │
214
- │ │ │ executor) │ │ shell aware) │
215
- └─────────────────┘ └─────────────────┘ └────────┬─────────┘
216
-
217
-
218
- ┌─────────────┐
219
- │ Result │
220
- │ (OOP with │
221
- │ CommandInfo│
222
- │ + Output │
223
- │ + Metadata)│
224
- └─────────────┘
1094
+ ukiryu system
225
1095
  ----
226
1096
 
227
- == Installation
1097
+ Shows:
1098
+ * Available shells on your system
1099
+ * All supported shells
1100
+ * Platform detection
228
1101
 
229
- Add this line to your application's Gemfile:
1102
+ === Manage Definitions
230
1103
 
231
- [source,ruby]
1104
+ ==== List All Definitions
1105
+
1106
+ [source,bash]
232
1107
  ----
233
- gem 'ukiryu'
1108
+ ukiryu definitions list
234
1109
  ----
235
1110
 
236
- And then execute:
1111
+ Shows all discovered definitions with source type and path.
237
1112
 
238
- [source,shell]
1113
+ ==== Show Definition Paths
1114
+
1115
+ [source,bash]
239
1116
  ----
240
- bundle install
1117
+ ukiryu definitions paths
241
1118
  ----
242
1119
 
243
- Or install it yourself as:
1120
+ Shows all search paths in priority order.
244
1121
 
245
- [source,shell]
1122
+ ==== Get Definition Information
1123
+
1124
+ [source,bash]
246
1125
  ----
247
- gem install ukiryu
1126
+ ukiryu definitions info inkscape
248
1127
  ----
249
1128
 
250
- == Command Line Interface
1129
+ Shows detailed information about a specific definition.
251
1130
 
252
- Ukiryu includes a command-line interface (CLI) for exploring and interacting with tool profiles. The CLI requires the `thor` gem.
1131
+ ==== Add Definition
253
1132
 
254
- === CLI Commands
1133
+ [source,bash]
1134
+ ----
1135
+ ukiryu definitions add ./mytool-1.0.yaml
1136
+ ----
255
1137
 
256
- ==== list
1138
+ Installs a definition file to the user definitions directory.
257
1139
 
258
- List all available tools in the registry:
1140
+ ==== Remove Definition
259
1141
 
260
- [source,shell]
1142
+ [source,bash]
261
1143
  ----
262
- ukiryu list
1144
+ ukiryu definitions remove mytool
263
1145
  ----
264
1146
 
265
- Example output:
1147
+ Removes a user definition.
266
1148
 
267
- [source,shell]
1149
+ ==== Validate Definition File
1150
+
1151
+ [source,bash]
268
1152
  ----
269
- Available tools (3):
270
- [✓] ghostscript v10.0.0
271
- [✓] inkscape v1.3
272
- [✗] imagemagick version unknown
1153
+ ukiryu validate --definition ./mytool.yaml
273
1154
  ----
274
1155
 
275
- ==== info
1156
+ Validates a definition file without installing it.
276
1157
 
277
- Show detailed information about a specific tool:
1158
+ ==== Initialize New Definition
278
1159
 
279
- [source,shell]
1160
+ [source,bash]
280
1161
  ----
281
- ukiryu info inkscape
1162
+ ukiryu init mytool
282
1163
  ----
283
1164
 
284
- Example output:
1165
+ Creates a new tool definition from a template.
285
1166
 
286
- [source,shell]
1167
+ [source,bash]
1168
+ ----
1169
+ ukiryu init --list-types
287
1170
  ----
288
- Tool: inkscape
289
- Display Name: Inkscape Vector Graphics Editor
290
- Version: 1.3
291
- Homepage: https://inkscape.org
292
1171
 
293
- Aliases: ink, inksc
1172
+ Lists available template types (basic, cli_tool, converter).
294
1173
 
295
- Version Detection:
296
- Command: --version
297
- Pattern: (\d+\.\d+)
298
- Modern Threshold: 1.0
1174
+ [source,bash]
1175
+ ----
1176
+ ukiryu init mytool --type cli_tool --output mytool.yaml
1177
+ ----
299
1178
 
300
- Search Paths:
301
- macos:
302
- - /Applications/Inkscape.app/Contents/MacOS/inkscape
303
- - /opt/homebrew/bin/inkscape
1179
+ Creates a CLI tool definition and saves to file.
1180
+
1181
+ // Developer Experience
1182
+ == Developer Experience
304
1183
 
305
- Profiles (1):
306
- default:
307
- Platforms: macos, linux
308
- Shells: bash, zsh, fish
309
- Version: any
1184
+ === Error Suggestions
310
1185
 
311
- Status: INSTALLED
312
- Executable: /opt/homebrew/bin/inkscape
313
- Detected Version: 1.3.2
1186
+ Ukiryu provides intelligent error suggestions to help you fix definition issues quickly:
1187
+
1188
+ [source,bash]
314
1189
  ----
1190
+ $ ukiryu validate --definition mytool.yaml
315
1191
 
316
- ==== commands
1192
+ Validation failed
317
1193
 
318
- List all commands available for a tool:
1194
+ - Unknown field: 'platfoms'
1195
+ Did you mean 'platforms'?
319
1196
 
320
- [source,shell]
321
- ----
322
- ukiryu commands imagemagick
1197
+ - Missing required field: 'name'
1198
+ Add a tool name: `name: mytool`
1199
+
1200
+ - Missing required field: 'profiles'
1201
+ Add at least one profile with platform and shell information
323
1202
  ----
324
1203
 
325
- Example output:
1204
+ === Template Generation
326
1205
 
327
- [source,shell]
1206
+ Ukiryu includes templates for common tool types to help you get started quickly:
1207
+
1208
+ [source,bash]
328
1209
  ----
329
- Commands for imagemagick:
330
- convert Convert between image formats
331
- Usage: magick convert [options] input output
332
- Subcommand: convert
333
- identify Describe image format and characteristics
334
- Usage: magick identify [options] input
1210
+ # Basic template (minimal)
1211
+ ukiryu init mytool --type basic --output mytool.yaml
1212
+
1213
+ # CLI tool template (with help/version commands)
1214
+ ukiryu init mycli --type cli_tool --output mycli.yaml
1215
+
1216
+ # Converter template (for file conversion tools)
1217
+ ukiryu init imgconv --type converter --output imgconv.yaml
335
1218
  ----
336
1219
 
337
- ==== opts
1220
+ ==== Template Types
338
1221
 
339
- Show options for a tool or specific command:
1222
+ ===== Basic Template
340
1223
 
341
- [source,shell]
1224
+ Minimal template with required fields only:
1225
+
1226
+ [source,yaml]
342
1227
  ----
343
- # Show all options for a tool
344
- ukiryu opts inkscape
1228
+ name: mytool
1229
+ version: "1.0"
1230
+ $schema: https://ukiryu.com/schema/1.2
345
1231
 
346
- # Show options for a specific command
347
- ukiryu opts imagemagick convert
1232
+ profiles:
1233
+ - name: default
1234
+ platforms: [linux, macos]
1235
+ shells: [bash, zsh]
348
1236
  ----
349
1237
 
350
- Example output:
1238
+ ===== CLI Tool Template
351
1239
 
352
- [source,shell]
1240
+ Standard CLI tool with common commands:
1241
+
1242
+ [source,yaml]
353
1243
  ----
354
- Options for imagemagick convert:
355
- Convert between image formats
1244
+ name: mytool
1245
+ version: "1.0"
1246
+ $schema: https://ukiryu.com/schema/1.2
1247
+
1248
+ display_name: Mytool
1249
+ description: A CLI tool for processing data
1250
+ homepage: https://example.com/mytool
1251
+
1252
+ version_detection:
1253
+ command: "--version"
1254
+ pattern: "(\\d+\\.\\d+)"
1255
+ modern_threshold: "1.0"
356
1256
 
357
- Arguments:
358
- inputs (filevariadic)
359
- Position: last
360
- Description: Input files to process
1257
+ search_paths:
1258
+ macos:
1259
+ - "/usr/local/bin/mytool"
1260
+ - "/opt/homebrew/bin/mytool"
1261
+ linux:
1262
+ - "/usr/bin/mytool"
1263
+ - "/usr/local/bin/mytool"
361
1264
 
362
- Options:
363
- --resize -resize
364
- Type: string
365
- Values: 50%, 100x100, 50x50!
1265
+ profiles:
1266
+ - name: default
1267
+ platforms: [linux, macos]
1268
+ shells: [bash, zsh]
1269
+ option_style: double_dash_equals
366
1270
 
367
- --quality -quality
368
- Type: integer
369
- Range: 0..100
370
- Description: JPEG/MIFF/PNG compression level
1271
+ commands:
1272
+ - name: help
1273
+ description: Show help information
371
1274
 
372
- Flags:
373
- -strip -strip (default: false)
374
- Remove all profiles and text attributes from image
1275
+ - name: version
1276
+ description: Show version information
375
1277
  ----
376
1278
 
377
- ==== exec
1279
+ ===== Converter Template
378
1280
 
379
- Execute a tool command inline with key=value parameters:
1281
+ File conversion tool with extensive options:
380
1282
 
381
- [source,shell]
1283
+ [source,yaml]
382
1284
  ----
383
- # With default command (shorthand)
384
- ukiryu exec ping host=127.0.0.1 count=1
1285
+ name: imagick
1286
+ version: "1.0"
1287
+ $schema: https://ukiryu.com/schema/1.2
385
1288
 
386
- # With explicit command
387
- ukiryu exec imagemagick convert inputs=input.jpg output=output.jpg resize=50%
1289
+ display_name: Imagick
1290
+ description: File conversion tool for various formats
388
1291
 
389
- # With output format
390
- ukiryu exec ping host=127.0.0.1 count=1 --format json
1292
+ profiles:
1293
+ - name: default
1294
+ platforms: [linux, macos]
1295
+ shells: [bash, zsh]
1296
+
1297
+ # Reusable environment variable sets
1298
+ env_var_sets:
1299
+ headless:
1300
+ - name: DISPLAY
1301
+ value: ''
1302
+ platforms: [macos, linux]
1303
+ description: Disable display for headless operation
391
1304
 
392
- # With output to file
393
- ukiryu exec ping host=127.0.0.1 count=1 --output result.yaml
1305
+ commands:
1306
+ - name: convert
1307
+ description: Convert files between formats
1308
+ use_env_vars: [headless]
394
1309
 
395
- # Read from stdin (pipe)
396
- echo '{"name": "value"}' | ukiryu exec jq --stdin filter="."
1310
+ arguments:
1311
+ - name: inputs
1312
+ type: file
1313
+ variadic: true
1314
+ min: 1
1315
+ description: Input file(s)
397
1316
 
398
- # Read from file via stdin parameter
399
- ukiryu exec jq stdin=@/path/to/data.json filter="."
1317
+ options:
1318
+ - name: output
1319
+ type: file
1320
+ cli: --output
1321
+ required: true
1322
+ description: Output file path
400
1323
 
401
- # Debug mode (shows detailed execution info to stderr)
402
- UKIRYU_DEBUG=1 ukiryu exec ping host=127.0.0.1 count=1
1324
+ - name: format
1325
+ type: symbol
1326
+ cli: --format
1327
+ values: [png, jpg, pdf, svg]
1328
+ description: Output format
403
1329
  ----
404
1330
 
405
- Available options:
1331
+ ==== Customizing Templates
406
1332
 
407
- * `--registry`, `-r`: Path to tool registry
408
- * `--format`, `-f`: Response format (`yaml`, `json`, `table`)
409
- * `--output`, `-o`: Output file for response (default: stdout)
410
- * `--dry-run`, `-d`: Show execution request without executing
411
- * `--shell`: Shell to use for command execution (`bash`, `zsh`, `fish`, `sh`, `powershell`, `cmd`)
412
- * `--stdin`: Read input from stdin and pass to command
1333
+ [source,bash]
1334
+ ----
1335
+ ukiryu init mytool \
1336
+ --type cli_tool \
1337
+ --version "2.0" \
1338
+ --platforms linux windows \
1339
+ --purpose "image processing" \
1340
+ --output mytool.yaml
1341
+ ----
413
1342
 
414
- ==== execute-file
1343
+ Template variables:
1344
+ * `tool_name` - The tool name (required)
1345
+ * `version` - Tool version (default: "1.0")
1346
+ * `platforms` - Target platforms (default: [linux, macos])
1347
+ * `shells` - Target shells (default: [bash, zsh])
1348
+ * `purpose` - Tool description (for cli_tool)
1349
+ * `file_types` - File types description (for converter)
415
1350
 
416
- Execute a tool command from a YAML request file:
1351
+ ==== Interactive Mode (Opt-In)
417
1352
 
418
- [source,shell]
1353
+ When `highline` or `tty-prompt` gems are available, Ukiryu provides interactive prompts:
1354
+
1355
+ [source,bash]
419
1356
  ----
420
- # Create request file
421
- cat > request.yaml << EOF
422
- tool: ping
423
- command: ping
424
- arguments:
425
- host: 127.0.0.1
426
- count: 1
427
- EOF
1357
+ $ ukiryu init
428
1358
 
429
- # Execute from file
430
- ukiryu execute-file request.yaml
431
- ====
1359
+ Select template type:
1360
+ 1) Basic: Minimal template with required fields only
1361
+ 2) CLI Tool: Standard CLI tool with common commands
1362
+ 3) Converter: File conversion tool with extensive options
1363
+ Selection [1-3] [Basic]:
432
1364
 
433
- ==== execute
1365
+ Tool name: [mytool]:
434
1366
 
435
- Execute a tool command with options:
1367
+ Version: [1.0]: 2.0
436
1368
 
437
- [source,shell]
438
- ----
439
- # Basic execution
440
- ukiryu execute imagemagick convert --resize 50x50 -i input.png -o output.jpg
1369
+ Save to file: [mytool.yaml]: /path/to/mytool.yaml
1370
+
1371
+ Definition saved to: /path/to/mytool.yaml
441
1372
 
442
- # Dry run (show command without executing)
443
- ukiryu execute inkscape export_pdf --dry-run -i input.svg -o output.pdf
1373
+ Next steps:
1374
+ 1. Review and edit the definition
1375
+ 2. Validate: ukiryu validate --definition /path/to/mytool.yaml
1376
+ 3. Install: ukiryu definitions add /path/to/mytool.yaml
444
1377
  ----
445
1378
 
446
- Example output:
1379
+ To enable interactive prompts, add one of these gems to your Gemfile:
447
1380
 
448
- [source,shell]
1381
+ [source,ruby]
449
1382
  ----
450
- Command completed successfully
451
- Exit status: 0
452
- Duration: 1.2s
1383
+ # For rich prompts (recommended)
1384
+ gem 'tty-prompt'
453
1385
 
454
- STDOUT:
1386
+ # Or use highline
1387
+ gem 'highline'
455
1388
  ----
456
1389
 
457
- ==== version
1390
+ // Advanced Features
1391
+ == Advanced Features
458
1392
 
459
- Show Ukiryu CLI version:
1393
+ === Version Constraints
460
1394
 
461
- [source,shell]
1395
+ Ukiryu supports semantic versioning constraints when selecting tool definitions:
1396
+
1397
+ [source,ruby]
462
1398
  ----
463
- ukiryu version
1399
+ # Use specific version
1400
+ tool = Ukiryu::Tool.get(:inkscape, version: '1.0')
1401
+
1402
+ # Use version constraint (pessimistic)
1403
+ # Any 1.x version, but less than 2.0
1404
+ constraint = '~> 1.0'
1405
+
1406
+ # Find available versions
1407
+ versions = Ukiryu::Definition::Discovery
1408
+ .definitions_for('inkscape')
1409
+ .map(&:version)
1410
+
1411
+ # Resolve constraint
1412
+ selected = Ukiryu::Definition::VersionResolver.resolve(
1413
+ constraint,
1414
+ versions
1415
+ )
464
1416
  ----
465
1417
 
466
- Example output:
1418
+ Supported constraint operators:
1419
+ * `1.0` - exact version
1420
+ * `>= 1.0` - minimum version (inclusive)
1421
+ * `> 1.0` - minimum version (exclusive)
1422
+ * `<= 2.0` - maximum version (inclusive)
1423
+ * `< 2.0` - maximum version (exclusive)
1424
+ * `~> 1.2` - pessimistic version constraint (>= 1.2.0, < 1.3.0)
1425
+ * `>= 1.0, < 2.0` - range constraint
467
1426
 
468
- [source,shell]
1427
+ === Tool Aliases
1428
+
1429
+ Create shortcuts for frequently used tools:
1430
+
1431
+ [source,bash]
469
1432
  ----
470
- Ukiryu version 0.1.0
1433
+ # Simple alias
1434
+ ukiryu alias add magick imagemagick
1435
+
1436
+ # Versioned alias
1437
+ ukiryu alias add gs7 ghostscript
1438
+
1439
+ # Command alias
1440
+ ukiryu alias add convert imagemagick:convert
471
1441
  ----
472
1442
 
473
- === Registry Configuration
1443
+ List all aliases:
1444
+
1445
+ [source,bash]
1446
+ ----
1447
+ $ ukiryu alias list
474
1448
 
475
- The CLI searches for tool profiles in the following locations:
1449
+ Tool Aliases:
1450
+ magick → imagemagick
1451
+ gs7 → ghostscript/7.0
1452
+ convert → imagemagick:convert
476
1453
 
477
- 1. Environment variable: `UKIRYU_REGISTRY`
478
- 2. Sibling `register/` directory relative to gem installation
479
- 3. `../register/` relative to current working directory (development)
1454
+ Total: 3 aliases
1455
+ ----
480
1456
 
481
- Set a custom registry path:
1457
+ Resolve an alias:
482
1458
 
483
- [source,shell]
1459
+ [source,bash]
484
1460
  ----
485
- export UKIRYU_REGISTRY=/path/to/register
486
- ukiryu list
1461
+ $ ukiryu alias resolve magick
1462
+
1463
+ Alias: magick
1464
+ Resolves to:
1465
+ Tool: imagemagick
1466
+ Definition lookup:
1467
+ ✓ Found: imagemagick/7.1 (bundled)
1468
+ Path: /usr/local/share/ukiryu/definitions/imagemagick/7.1.yaml
487
1469
  ----
488
1470
 
489
- Or use the `--registry` option:
1471
+ Remove an alias:
490
1472
 
491
- [source,shell]
1473
+ [source,bash]
492
1474
  ----
493
- ukiryu list --registry /path/to/register
494
- ukiryu info inkscape -r /path/to/register
1475
+ ukiryu alias remove magick
495
1476
  ----
496
1477
 
497
- == Usage
1478
+ === Definition Caching
498
1479
 
499
- === Basic Command Execution
1480
+ Ukiryu automatically caches tool definitions for performance:
500
1481
 
501
- [source,ruby]
1482
+ [source,bash]
502
1483
  ----
503
- require 'ukiryu'
504
-
505
- # Set registry path to tool profiles
506
- Ukiryu::Registry.default_registry_path = 'path/to/register'
1484
+ # View cache information
1485
+ ukiryu cache info
507
1486
 
508
- # Get a tool
509
- tool = Ukiryu::Tool.get(:imagemagick)
1487
+ Status: Active
1488
+ Entries: 5
1489
+ TTL: 300 seconds
1490
+ Refresh Strategy: lazy
510
1491
 
511
- # Execute a command
512
- result = tool.execute(:convert, {
513
- inputs: ['input.png'],
514
- output: 'output.jpg',
515
- resize: '50x50',
516
- quality: 85
517
- })
1492
+ # Show detailed statistics
1493
+ ukiryu cache stats
518
1494
 
519
- # Check result
520
- if result.success?
521
- puts "Converted in #{result.metadata.formatted_duration}"
522
- else
523
- puts "Error: #{result.output.stderr}"
524
- end
1495
+ # Clear the cache
1496
+ ukiryu cache clear
525
1497
  ----
526
1498
 
527
- === Passing Stdin
1499
+ Cache behavior:
1500
+ * **Lazy mode** (default) - Checks staleness on each access, refreshes if needed
1501
+ * **Eager mode** - Background thread periodically refreshes all cache
1502
+ * **Never mode** - Disables auto-refresh, manual clear only
528
1503
 
529
- Pass data to commands via stdin using the `:stdin` parameter:
1504
+ === Definition Resolution
530
1505
 
531
- [source,ruby]
1506
+ See which definition would be used for a tool:
1507
+
1508
+ [source,bash]
532
1509
  ----
533
- # Pass string data to stdin
534
- result = tool.execute(:process, {
535
- stdin: '{"name": "value"}',
536
- filter: '.'
537
- })
1510
+ $ ukiryu resolve inkscape
538
1511
 
539
- # Read from file and pass to stdin
540
- result = tool.execute(:process, {
541
- stdin: File.read('/path/to/data.json'),
542
- filter: '.name'
543
- })
1512
+ Resolution for: inkscape
544
1513
 
545
- # Pass IO object (file, pipe, etc.)
546
- File.open('/path/to/data.json', 'r') do |file|
547
- result = tool.execute(:process, {
548
- stdin: file,
549
- filter: '.'
550
- })
551
- end
552
- ----
1514
+ Available Definitions (2):
1515
+ ★ 1.0 (user) Priority: 1
1516
+ Path: ~/.local/share/ukiryu/definitions/inkscape/1.0.yaml
1517
+ Mtime: 2024-01-15 10:30:00 +0800
553
1518
 
554
- The `:stdin` parameter is a special execution parameter that is extracted before building command arguments and written to the child process's stdin stream.
1519
+ 0.92 (system) Priority: 4
1520
+ Path: /usr/share/ukiryu/definitions/inkscape/0.92.yaml
1521
+ Mtime: 2023-12-01 15:20:00 +0800
555
1522
 
556
- === Accessing Result Details
1523
+ Selected Definition (highest priority):
1524
+ ✓ inkscape/1.0
1525
+ Source: user
1526
+ Path: ~/.local/share/ukiryu/definitions/inkscape/1.0.yaml
1527
+ Priority: 1
1528
+ ----
557
1529
 
558
- [source,ruby]
1530
+ Resolve with version constraint:
1531
+
1532
+ [source,bash]
559
1533
  ----
560
- result = tool.execute(:identify, { input: ['test.png'] })
1534
+ $ ukiryu resolve imagemagick ">= 7.0"
561
1535
 
562
- # Command information
563
- result.command_info.executable # Full path to executable
564
- result.command_info.arguments # Array of arguments
565
- result.command_info.full_command # Complete command string
566
- result.command_info.shell # Shell type used
1536
+ Resolution for: imagemagick
567
1537
 
568
- # Output
569
- result.output.stdout # Stripped stdout
570
- result.output.stderr # Stripped stderr
571
- result.output.stdout_lines # Array of lines
572
- result.output.stdout_contains?(/PNG/)
1538
+ Available Definitions (3):
1539
+ ★ 7.1 (bundled) Priority: 2
1540
+ ★ 7.0 (system) Priority: 4
1541
+ □ 6.0 (system) Priority: 4
573
1542
 
574
- # Metadata
575
- result.metadata.started_at # Time object
576
- result.metadata.finished_at # Time object
577
- result.metadata.duration # Float (seconds)
578
- result.metadata.timed_out? # Boolean
1543
+ Selected Definition (constraint: >= 7.0):
1544
+ ✓ imagemagick/7.1
579
1545
  ----
580
1546
 
581
- === Error Handling
1547
+ Priority icons:
1548
+ * `★` - User definitions
1549
+ * `◆` - Bundled definitions
1550
+ * `■` - Local system definitions
1551
+ * `□` - System definitions
1552
+ * `△` - Register definitions
582
1553
 
583
- [source,ruby]
584
- ----
585
- begin
586
- result = tool.execute(:convert, { inputs: ['nonexistent.png'], output: 'out.jpg' })
587
- rescue Ukiryu::ExecutionError => e
588
- puts "Command failed: #{e.message}"
589
- rescue Ukiryu::TimeoutError => e
590
- puts "Command timed out: #{e.message}"
591
- end
592
- ----
1554
+ === Definition Composition
593
1555
 
594
- === Tool Availability
1556
+ Definition composition allows building complex definitions from simpler ones:
595
1557
 
596
- [source,ruby]
1558
+ [source,yaml]
597
1559
  ----
598
- tool = Ukiryu::Tool.get(:ffmpeg)
1560
+ name: mytool
1561
+ version: "2.0"
599
1562
 
600
- if tool.available?
601
- puts "FFmpeg #{tool.version} found at #{tool.executable}"
602
- else
603
- puts "FFmpeg not installed"
604
- end
1563
+ # Include common profiles from another definition
1564
+ includes:
1565
+ - tool: common_tools
1566
+ profile: default
1567
+ commands: [help, version]
1568
+
1569
+ profiles:
1570
+ - name: default
1571
+ platforms: [linux, macos]
1572
+ shells: [bash, zsh]
1573
+
1574
+ # Commands from includes are merged here
1575
+ # Add tool-specific commands
1576
+ commands:
1577
+ - name: mycommand
1578
+ description: My custom command
605
1579
  ----
606
1580
 
607
- [[oop-api]]
608
- == OOP API
1581
+ === Shadowing and Override Behavior
609
1582
 
610
- Ukiryu provides a fully object-oriented API for tool interaction. Tool-specific classes are dynamically generated from YAML profiles and available under `Ukiryu::Tools::*`.
1583
+ When multiple definitions exist for the same tool, the highest priority definition is used:
611
1584
 
612
- === Tool Classes
1585
+ 1. User definitions (`~/.local/share/ukiryu/definitions/`)
1586
+ 2. Tool-bundled definitions
1587
+ 3. Local system definitions (`/usr/local/share/ukiryu/definitions/`)
1588
+ 4. System definitions (`/usr/share/ukiryu/definitions/`)
1589
+ 5. Register definitions
613
1590
 
614
- Tool classes are generated automatically when first accessed:
1591
+ To explicitly use a specific definition source:
615
1592
 
616
- [source,ruby]
1593
+ [source,bash]
617
1594
  ----
618
- # Access a tool class (autogenerated on first access)
619
- tool = Ukiryu::Tools::Imagemagick.new
1595
+ # Use only system definitions
1596
+ ukiryu exec --system inkscape export
620
1597
 
621
- # Check availability and version
622
- tool.available? # => true
623
- tool.version # => "7.1"
624
-
625
- # List available commands
626
- tool.class.profile[:commands].keys
627
- # => [:convert, :identify, :mogrify, ...]
1598
+ # Use specific definition file
1599
+ ukiryu exec --definition /path/to/inkscape.yaml inkscape export
628
1600
  ----
629
1601
 
630
- Available tool classes include:
631
- * `Ukiryu::Tools::Imagemagick`
632
- * `Ukiryu::Tools::Ffmpeg`
633
- * `Ukiryu::Tools::Pandoc`
634
- * `Ukiryu::Tools::Ghostscript`
635
- * `Ukiryu::Tools::Inkscape`
636
- * `Ukiryu::Tools::Optipng`
637
- * `Ukiryu::Tools::Jpegoptim`
1602
+ // Key Features
1603
+ == Key Features
638
1604
 
639
- === Options Classes
1605
+ === Structured Responses
640
1606
 
641
- Options classes are generated per-command and provide a fluent interface:
1607
+ No more parsing stdout/stderr:
642
1608
 
643
1609
  [source,ruby]
644
1610
  ----
645
- # Create an options object
646
- convert_options = tool.options_for(:convert)
1611
+ result = tool.execute(:export, params)
1612
+
1613
+ # Rich result object
1614
+ result.command_info.executable # Full path
1615
+ result.command_info.full_command # Complete command
1616
+ result.output.success? # Boolean
1617
+ result.output.stdout # Stripped stdout
1618
+ result.output.stderr # Stripped stderr
1619
+ result.metadata.duration # Float seconds
1620
+ result.metadata.formatted_duration # "500ms"
1621
+ ----
647
1622
 
648
- # Set options individually
649
- convert_options.inputs = ["input.png"]
650
- convert_options.resize = "50%"
651
- convert_options.quality = 85
652
- convert_options.strip = true
653
- convert_options.output = "output.jpg"
1623
+ === Type Safety
654
1624
 
655
- # Or use set() for batch assignment
656
- convert_options.set(
657
- inputs: ["input.png"],
658
- resize: "50%",
659
- quality: 85,
660
- strip: true
661
- )
662
- convert_options.output = "output.jpg"
1625
+ Parameters are validated against the schema:
663
1626
 
664
- # Execute the command
665
- result = convert_options.run
1627
+ [source,ruby]
1628
+ ----
1629
+ # Valid
1630
+ tool.execute(:export, {
1631
+ format: :png, # Must be in values list
1632
+ dpi: 300, # Must be in range
1633
+ width: 1024 # Must be integer
1634
+ })
666
1635
 
667
- # Check the result
668
- puts result.success? # => true
669
- puts result.exit_code # => 0
670
- puts result.stdout # => ""
671
- puts result.stderr # => ""
672
- puts result.duration # => 0.5
1636
+ # Invalid - raises Ukiryu::TypeError
1637
+ tool.execute(:export, {
1638
+ format: :unsupported, # Type error!
1639
+ dpi: -5 # Range error!
1640
+ })
673
1641
  ----
674
1642
 
675
- === Response Classes
1643
+ === Platform Adaptation
676
1644
 
677
- Response classes wrap execution results in a structured object:
1645
+ Same code works everywhere:
678
1646
 
679
1647
  [source,ruby]
680
1648
  ----
681
- result = convert_options.run
1649
+ # Works on macOS, Linux, Windows
1650
+ tool.execute(:export, {
1651
+ inputs: ['~/drawing.svg'], # Tilde expansion
1652
+ output: ['~/drawing.png'],
1653
+ format: :png
1654
+ })
1655
+
1656
+ # Paths are automatically formatted:
1657
+ # macOS: /Users/user/drawing.png
1658
+ # Windows: C:\Users\user\drawing.png
1659
+ ----
682
1660
 
683
- # Status information
684
- result.success? # => true
685
- result.exit_code # => 0
1661
+ === Version Management
686
1662
 
687
- # Output
688
- result.stdout # Stripped stdout
689
- result.stderr # Stripped stderr
690
- result.stdout_lines # Array of lines
1663
+ Support multiple tool versions with automatic selection:
691
1664
 
692
- # Timing
693
- result.duration # Float (seconds)
694
- result.formatted_duration # "500ms"
1665
+ [source,yaml]
1666
+ ----
1667
+ register/
1668
+ ├── tools/
1669
+ │ ├── inkscape/
1670
+ │ │ ├── 1.0.yaml # Modern Inkscape
1671
+ │ │ └── 0.92.yaml # Legacy Inkscape
1672
+ │ └── imagemagick/
1673
+ │ ├── 7.1.yaml # Current ImageMagick
1674
+ │ └── 6.0.yaml # Old ImageMagick
1675
+ ----
695
1676
 
696
- # Full command info
697
- result.executable # Full path to executable
698
- result.arguments # Array of arguments
699
- result.full_command # Complete command string
1677
+ [source,ruby]
1678
+ ----
1679
+ # Automatically selects correct profile
1680
+ tool = Ukiryu::Tool.get(:inkscape)
1681
+ # If Inkscape 1.3+ detected → uses 1.0.yaml
1682
+ # If Inkscape 0.9.x detected → uses 0.92.yaml
700
1683
  ----
701
1684
 
702
- === Action Pattern
1685
+ === Headless Mode
703
1686
 
704
- An alternative pattern using action classes:
1687
+ GUI applications run without windows on servers:
705
1688
 
706
1689
  [source,ruby]
707
1690
  ----
708
- # Create an action
709
- action = tool.action_for(:convert)
710
-
711
- # Get options object
712
- action_options = action.options
713
- action_options.set(
714
- inputs: ["input.png"],
715
- resize: "50%"
716
- )
717
- action_options.output = "output.jpg"
1691
+ # Automatically runs in headless mode
1692
+ tool = Ukiryu::Tool.get(:inkscape)
1693
+ result = tool.execute(:export, {
1694
+ inputs: ['drawing.svg'],
1695
+ output: 'drawing.png'
1696
+ })
1697
+ # No GUI window appears! Perfect for servers and CI/CD
1698
+ ----
718
1699
 
719
- # Execute with the action
720
- result = action.run(action_options)
1700
+ Check headless support:
721
1701
 
722
- # Or pass a hash directly (action creates options)
723
- result = action.run(
724
- inputs: ["input.png"],
725
- resize: "50%",
726
- output: "output.jpg"
727
- )
1702
+ [source,bash]
1703
+ ----
1704
+ ukiryu info inkscape | grep "Execution Mode"
1705
+ # Shows: Execution Mode: headless
728
1706
  ----
729
1707
 
730
- === Manual Option Injection
1708
+ === Error Handling
731
1709
 
732
- When tool definitions don't cover all options, use `extra_args`:
1710
+ Structured exceptions instead of magic exit codes:
733
1711
 
734
1712
  [source,ruby]
735
1713
  ----
736
- # Pass undefined options via extra_args
737
- convert_options = tool.options_for(:convert)
738
- convert_options.set(
739
- inputs: ["input.png"],
740
- resize: "50%",
741
- extra_args: ["-strip", "-quality", "95", "-interlace", "Plane"]
742
- )
743
- convert_options.output = "output.jpg"
1714
+ begin
1715
+ result = tool.execute(:export, params)
1716
+ rescue Ukiryu::ToolNotFoundError => e
1717
+ puts "Tool not found: #{e.tool_name}"
1718
+ puts "Available tools: #{Ukiryu::Register.tools.join(', ')}"
1719
+ rescue Ukiryu::ExecutionError => e
1720
+ puts "Command failed!"
1721
+ puts "Exit code: #{e.result.exit_status}"
1722
+ puts "Stderr: #{e.result.stderr}"
1723
+ puts "Stdout: #{e.result.stdout}"
1724
+ rescue Ukiryu::TimeoutError => e
1725
+ puts "Command timed out after #{e.timeout}s"
1726
+ end
1727
+ ----
1728
+
1729
+ === Exit Code Semantics
744
1730
 
745
- result = convert_options.run
1731
+ Exit codes are documented in the profile:
1732
+
1733
+ [source,yaml]
1734
+ ----
1735
+ exit_codes:
1736
+ standard:
1737
+ "0": "Success"
1738
+ "1": "File not found"
1739
+ "3": "Initialization error"
1740
+ custom:
1741
+ "2": "Export failed (see stderr for details)"
746
1742
  ----
747
1743
 
748
- The `extra_args` parameter accepts either an array of strings or a single string. All arguments are properly escaped for the detected shell.
1744
+ // Ecosystem Tools
1745
+ == Ecosystem Tools
749
1746
 
750
- === Complete Examples
1747
+ Ukiryu provides tools for validating, linting, and documenting tool definitions.
751
1748
 
752
- .Batch Image Conversion
753
- [source,ruby]
1749
+ === Definition Validation
1750
+
1751
+ Validate definitions against JSON Schema and structural rules:
1752
+
1753
+ [source,bash]
754
1754
  ----
755
- require 'ukiryu'
1755
+ # Validate a single file
1756
+ ukiryu validate file path/to/tool.yaml
756
1757
 
757
- Ukiryu::Registry.default_registry_path = 'path/to/register'
1758
+ # Validate all definitions in register
1759
+ ukiryu validate all
758
1760
 
759
- im = Ukiryu::Tools::Imagemagick.new
1761
+ # Validate with custom schema
1762
+ ukiryu validate file path/to/tool.yaml --schema path/to/schema.json
760
1763
 
761
- Dir["*.tif"].each do |tif_file|
762
- png_file = tif_file.sub('.tif', '.png')
1764
+ # Test executable against actual tool (smoke test)
1765
+ ukiryu validate file path/to/tool.yaml --executable
763
1766
 
764
- im.options_for(:convert).tap do |opts|
765
- opts.set(inputs: [tif_file], resize: "50%")
766
- opts.output = png_file
767
- opts.run
768
- end
1767
+ # Strict mode (warnings as errors)
1768
+ ukiryu validate file path/to/tool.yaml --strict
769
1769
 
770
- puts "Converted #{tif_file} -> #{png_file}"
771
- end
1770
+ # JSON output for CI/CD
1771
+ ukiryu validate file path/to/tool.yaml --format json
772
1772
  ----
773
1773
 
774
- .Video Processing with FFmpeg
1774
+ The `--executable` flag performs a smoke test against the actual tool:
1775
+
1776
+ * Checks if the tool executable can be found
1777
+ * Tests version detection (if defined)
1778
+ * Tests basic command execution
1779
+
1780
+ Programmatic usage:
1781
+
775
1782
  [source,ruby]
776
1783
  ----
777
- ffmpeg = Ukiryu::Tools::Ffmpeg.new
1784
+ result = Ukiryu::Definition::DefinitionValidator.validate_file('tool.yaml')
1785
+
1786
+ if result.valid?
1787
+ puts "✓ Valid"
1788
+ else
1789
+ puts "✗ Invalid"
1790
+ result.errors.each { |e| puts " - #{e}" }
1791
+ end
778
1792
 
779
- # Convert video to MP4 with custom codec settings
780
- ffmpeg.options_for(:transcode).tap do |opts|
781
- opts.set(
782
- inputs: ["input.mov"],
783
- video_codec: "libx264",
784
- bitrate: "2M",
785
- extra_args: ["-preset", "slow", "-crf", "22"]
786
- )
787
- opts.output = "output.mp4"
788
- opts.run
1793
+ # Check for warnings
1794
+ if result.has_warnings?
1795
+ result.warnings.each { |w| puts " Warning: #{w}" }
789
1796
  end
790
1797
  ----
791
1798
 
792
- .Document Conversion with Pandoc
793
- [source,ruby]
1799
+ === Definition Linting
1800
+
1801
+ Check definitions for best practices and potential issues:
1802
+
1803
+ [source,bash]
794
1804
  ----
795
- pandoc = Ukiryu::Tools::Pandoc.new
1805
+ # Lint a definition file
1806
+ ukiryu lint file path/to/tool.yaml
796
1807
 
797
- # Convert Markdown to PDF
798
- pandoc.options_for(:convert).tap do |opts|
799
- opts.set(
800
- inputs: ["document.md"],
801
- from: "markdown",
802
- to: "pdf",
803
- standalone: true,
804
- extra_args: ["--pdf-engine", "xelatex", "-V", "geometry:margin=1in"]
805
- )
806
- opts.output = "document.pdf"
807
- result = opts.run
1808
+ # Lint all definitions
1809
+ ukiryu lint all
808
1810
 
809
- puts "Conversion #{result.success? ? 'succeeded' : 'failed'}"
810
- end
1811
+ # List all linting rules
1812
+ ukiryu lint rules
811
1813
  ----
812
1814
 
813
- .SVG Export with Inkscape
1815
+ Programmatic usage:
1816
+
814
1817
  [source,ruby]
815
1818
  ----
816
- inkscape = Ukiryu::Tools::Inkscape.new
1819
+ result = Ukiryu::Definition::DefinitionLinter.lint_file('tool.yaml')
1820
+
1821
+ # Access issues by severity
1822
+ result.errors.each { |e| puts "ERROR: #{e.message}" }
1823
+ result.warnings.each { |w| puts "WARNING: #{w.message}" }
817
1824
 
818
- # Export SVG to PNG at specific DPI
819
- inkscape.options_for(:export).tap do |opts|
820
- opts.set(
821
- inputs: ["design.svg"],
822
- dpi: 300,
823
- export_area: "page"
824
- )
825
- opts.output = "design.png"
826
- opts.run
1825
+ # Get suggestions for fixes
1826
+ result.issues.each do |issue|
1827
+ puts "#{issue.message}"
1828
+ puts " Suggestion: #{issue.suggestion}" if issue.has_suggestion?
827
1829
  end
828
1830
  ----
829
1831
 
830
- == Tool Profiles
1832
+ Linting rules include:
831
1833
 
832
- Tool profiles are defined in separate https://github.com/ukiryu/register[Ukiryu Register] repository.
1834
+ * **Naming conventions** - Tool and command name format checks
1835
+ * **Completeness** - Missing descriptions, homepages, version detection
1836
+ * **Security** - Dangerous shell commands, unvalidated user input
1837
+ * **Best practices** - Redundant profiles, missing platform specifications
833
1838
 
834
- Example profile structure:
1839
+ === Documentation Generation
835
1840
 
836
- [source,yaml]
1841
+ Generate human-readable documentation from definitions:
1842
+
1843
+ [source,bash]
837
1844
  ----
838
- name: imagemagick
839
- version: "7.1"
1845
+ # Generate docs for a tool
1846
+ ukiryu docs generate imagemagick
840
1847
 
841
- version_detection:
842
- command: "--version"
843
- pattern: "(\\d+\\.\\d+)"
1848
+ # Generate docs to file
1849
+ ukiryu docs generate imagemagick --output imagemagick.md
844
1850
 
845
- profiles:
846
- - name: unix
847
- platforms: [macos, linux]
848
- shells: [bash, zsh, fish, sh]
849
- commands:
850
- convert:
851
- arguments:
852
- - name: inputs
853
- type: file
854
- variadic: true
855
- position: last
856
- options:
857
- - name: resize
858
- cli: "-resize"
859
- flags:
860
- - name: strip
861
- cli: "-strip"
1851
+ # Generate docs for all tools
1852
+ ukiryu docs generate-all --output-dir docs/
1853
+
1854
+ # Choose format (markdown or asciidoc)
1855
+ ukiryu docs generate imagemagick --format asciidoc
1856
+ ----
1857
+
1858
+ Programmatic usage:
1859
+
1860
+ [source,ruby]
1861
+ ----
1862
+ docs = Ukiryu::Definition::DocumentationGenerator.generate(
1863
+ definition,
1864
+ format: :markdown
1865
+ )
1866
+
1867
+ puts docs
1868
+ # Outputs:
1869
+ # # imagemagick
1870
+ #
1871
+ # Version: 7.1
1872
+ #
1873
+ # ## Commands
1874
+ #
1875
+ # ### `convert`
1876
+ # Convert between image formats...
862
1877
  ----
863
1878
 
1879
+ // Documentation
1880
+ == Documentation
1881
+
1882
+ Comprehensive documentation is available at:
1883
+
1884
+ * https://ukiryu.com[Ukiryu Documentation Site]
1885
+ * https://github.com/ukiryu/ukiryu[GitHub Repository]
1886
+ * https://github.com/ukiryu/register[Ukiryu Tool Register]
1887
+
1888
+ // Development
864
1889
  == Development
865
1890
 
866
1891
  === Running Tests
@@ -883,7 +1908,7 @@ Tests run on:
883
1908
  * Ubuntu latest
884
1909
  * macOS latest
885
1910
 
886
- == Contributing
1911
+ === Contributing
887
1912
 
888
1913
  1. Fork the repository
889
1914
  2. Create your feature branch
@@ -891,18 +1916,20 @@ Tests run on:
891
1916
  4. Ensure all tests pass
892
1917
  5. Submit a pull request
893
1918
 
1919
+ See link:CONTRIBUTING.adoc[CONTRIBUTING] for details.
894
1920
 
1921
+ // License
895
1922
  == License
896
1923
 
897
- The content is available as open source under the terms of the Ribose BSD
898
- 2-Clause License.
1924
+ The content is available as open source under the terms of the Ribose BSD 2-Clause License.
899
1925
 
1926
+ // Copyright
900
1927
  == Copyright
901
1928
 
902
1929
  Copyright Ribose.
903
1930
 
904
-
1931
+ // Related Repositories
905
1932
  == Related Repositories
906
1933
 
907
- * https://github.com/ukiryu/register[ukiryu/register] - Tool profile registry
1934
+ * https://github.com/ukiryu/register[ukiryu/register] - Tool profile register
908
1935
  * https://github.com/ukiryu/schema[ukiryu/schema] - Profile validation schema