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.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +58 -14
- data/.gitignore +3 -0
- data/.rubocop_todo.yml +170 -79
- data/Gemfile +1 -1
- data/README.adoc +1603 -576
- data/docs/.gitignore +1 -0
- data/docs/Gemfile +10 -0
- data/docs/INDEX.adoc +261 -0
- data/docs/_config.yml +180 -0
- data/docs/advanced/custom-tool-classes.adoc +581 -0
- data/docs/advanced/index.adoc +20 -0
- data/docs/features/configuration.adoc +657 -0
- data/docs/features/index.adoc +31 -0
- data/docs/features/platform-support.adoc +488 -0
- data/docs/getting-started/core-concepts.adoc +666 -0
- data/docs/getting-started/index.adoc +36 -0
- data/docs/getting-started/installation.adoc +216 -0
- data/docs/getting-started/quick-start.adoc +258 -0
- data/docs/guides/env-var-sets.adoc +388 -0
- data/docs/guides/index.adoc +20 -0
- data/docs/interfaces/cli.adoc +609 -0
- data/docs/interfaces/index.adoc +153 -0
- data/docs/interfaces/ruby-api.adoc +538 -0
- data/docs/lychee.toml +49 -0
- data/docs/reference/configuration-options.adoc +720 -0
- data/docs/reference/error-codes.adoc +634 -0
- data/docs/reference/index.adoc +20 -0
- data/docs/reference/ruby-api.adoc +1217 -0
- data/docs/understanding/index.adoc +20 -0
- data/lib/ukiryu/cli.rb +43 -58
- data/lib/ukiryu/cli_commands/base_command.rb +16 -27
- data/lib/ukiryu/cli_commands/cache_command.rb +100 -0
- data/lib/ukiryu/cli_commands/commands_command.rb +8 -8
- data/lib/ukiryu/cli_commands/commands_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/config_command.rb +49 -7
- data/lib/ukiryu/cli_commands/definitions_command.rb +254 -0
- data/lib/ukiryu/cli_commands/describe_command.rb +13 -7
- data/lib/ukiryu/cli_commands/describe_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/docs_command.rb +148 -0
- data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/extract_command.rb +2 -2
- data/lib/ukiryu/cli_commands/info_command.rb +7 -7
- data/lib/ukiryu/cli_commands/lint_command.rb +167 -0
- data/lib/ukiryu/cli_commands/list_command.rb +6 -6
- data/lib/ukiryu/cli_commands/opts_command.rb +2 -2
- data/lib/ukiryu/cli_commands/opts_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/register_command.rb +144 -0
- data/lib/ukiryu/cli_commands/resolve_command.rb +124 -0
- data/lib/ukiryu/cli_commands/run_command.rb +38 -14
- data/lib/ukiryu/cli_commands/run_file_command.rb +2 -2
- data/lib/ukiryu/cli_commands/system_command.rb +50 -32
- data/lib/ukiryu/cli_commands/validate_command.rb +452 -51
- data/lib/ukiryu/cli_commands/which_command.rb +5 -5
- data/lib/ukiryu/command_builder.rb +81 -23
- data/lib/ukiryu/config/env_provider.rb +3 -3
- data/lib/ukiryu/config/env_schema.rb +6 -6
- data/lib/ukiryu/config.rb +11 -11
- data/lib/ukiryu/definition/definition_cache.rb +238 -0
- data/lib/ukiryu/definition/definition_composer.rb +257 -0
- data/lib/ukiryu/definition/definition_linter.rb +460 -0
- data/lib/ukiryu/definition/definition_validator.rb +320 -0
- data/lib/ukiryu/definition/discovery.rb +239 -0
- data/lib/ukiryu/definition/documentation_generator.rb +429 -0
- data/lib/ukiryu/definition/lint_issue.rb +168 -0
- data/lib/ukiryu/definition/loader.rb +139 -0
- data/lib/ukiryu/definition/metadata.rb +159 -0
- data/lib/ukiryu/definition/source.rb +87 -0
- data/lib/ukiryu/definition/sources/file.rb +138 -0
- data/lib/ukiryu/definition/sources/string.rb +88 -0
- data/lib/ukiryu/definition/validation_result.rb +158 -0
- data/lib/ukiryu/definition/version_resolver.rb +194 -0
- data/lib/ukiryu/definition.rb +40 -0
- data/lib/ukiryu/errors.rb +6 -0
- data/lib/ukiryu/execution_context.rb +11 -11
- data/lib/ukiryu/executor.rb +6 -0
- data/lib/ukiryu/extractors/extractor.rb +6 -5
- data/lib/ukiryu/extractors/help_parser.rb +13 -19
- data/lib/ukiryu/logger.rb +3 -1
- data/lib/ukiryu/models/command_definition.rb +3 -3
- data/lib/ukiryu/models/command_info.rb +1 -1
- data/lib/ukiryu/models/components.rb +1 -3
- data/lib/ukiryu/models/env_var_definition.rb +11 -3
- data/lib/ukiryu/models/flag_definition.rb +15 -0
- data/lib/ukiryu/models/option_definition.rb +7 -7
- data/lib/ukiryu/models/platform_profile.rb +6 -3
- data/lib/ukiryu/models/routing.rb +1 -1
- data/lib/ukiryu/models/tool_definition.rb +2 -4
- data/lib/ukiryu/models/tool_metadata.rb +6 -6
- data/lib/ukiryu/models/validation_result.rb +1 -1
- data/lib/ukiryu/models/version_compatibility.rb +6 -3
- data/lib/ukiryu/models/version_detection.rb +10 -1
- data/lib/ukiryu/{registry.rb → register.rb} +54 -38
- data/lib/ukiryu/register_auto_manager.rb +268 -0
- data/lib/ukiryu/schema_validator.rb +31 -10
- data/lib/ukiryu/shell/base.rb +18 -0
- data/lib/ukiryu/shell/bash.rb +19 -1
- data/lib/ukiryu/shell/cmd.rb +11 -1
- data/lib/ukiryu/shell/powershell.rb +11 -1
- data/lib/ukiryu/shell.rb +1 -1
- data/lib/ukiryu/tool.rb +107 -95
- data/lib/ukiryu/tool_index.rb +22 -22
- data/lib/ukiryu/tools/base.rb +12 -25
- data/lib/ukiryu/tools/generator.rb +7 -7
- data/lib/ukiryu/tools.rb +3 -3
- data/lib/ukiryu/type.rb +20 -5
- data/lib/ukiryu/version.rb +1 -1
- data/lib/ukiryu/version_detector.rb +21 -2
- data/lib/ukiryu.rb +6 -3
- data/ukiryu-proposal.md +41 -41
- data/ukiryu.gemspec +1 -0
- metadata +64 -8
- data/.gitmodules +0 -3
data/README.adoc
CHANGED
|
@@ -1,866 +1,1891 @@
|
|
|
1
1
|
= Ukiryu
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
895
|
+
# Get definitions for a specific tool
|
|
896
|
+
inkscape_defs = Ukiryu::Definition::Discovery.definitions_for('inkscape')
|
|
11
897
|
|
|
12
|
-
|
|
898
|
+
# Find the best definition (highest priority, latest version)
|
|
899
|
+
metadata = Ukiryu::Definition::Discovery.find('inkscape')
|
|
13
900
|
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
904
|
+
# List all available tool names
|
|
905
|
+
tools = Ukiryu::Definition::Discovery.available_tools
|
|
906
|
+
# => ["inkscape", "imagemagick", "mytool"]
|
|
907
|
+
----
|
|
21
908
|
|
|
22
|
-
|
|
909
|
+
==== Definition Metadata
|
|
23
910
|
|
|
24
|
-
|
|
911
|
+
Each discovered definition has associated metadata:
|
|
25
912
|
|
|
26
913
|
[source,ruby]
|
|
27
914
|
----
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
931
|
+
==== Source Type Priority
|
|
43
932
|
|
|
44
|
-
|
|
933
|
+
Source types determine definition priority:
|
|
45
934
|
|
|
46
935
|
[source,ruby]
|
|
47
936
|
----
|
|
48
|
-
#
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
947
|
+
==== Version Sorting
|
|
69
948
|
|
|
70
|
-
|
|
949
|
+
Definitions are sorted by version within each priority level:
|
|
71
950
|
|
|
72
|
-
[source,
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
967
|
+
# Show where definitions are searched
|
|
968
|
+
ukiryu definitions paths
|
|
87
969
|
|
|
88
|
-
|
|
970
|
+
# Get information about a specific definition
|
|
971
|
+
ukiryu definitions info inkscape
|
|
89
972
|
|
|
90
|
-
|
|
973
|
+
# Install a definition to user directory
|
|
974
|
+
ukiryu definitions add ./mytool-1.0.yaml
|
|
91
975
|
|
|
92
|
-
|
|
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
|
-
|
|
98
|
-
echo '{"name": "value"}' | ukiryu exec jq stdin=- filter="."
|
|
980
|
+
===== List Definitions
|
|
99
981
|
|
|
100
|
-
|
|
101
|
-
|
|
982
|
+
[source,bash]
|
|
983
|
+
----
|
|
984
|
+
$ ukiryu definitions list
|
|
102
985
|
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
992
|
+
===== Show Paths
|
|
108
993
|
|
|
109
|
-
[source,
|
|
994
|
+
[source,bash]
|
|
110
995
|
----
|
|
111
|
-
|
|
112
|
-
cat large_data.json | ukiryu exec jq --stdin filter=".name" | sort
|
|
996
|
+
$ ukiryu definitions paths
|
|
113
997
|
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
=== OOP API
|
|
1004
|
+
===== Definition Info
|
|
121
1005
|
|
|
122
|
-
|
|
1006
|
+
[source,bash]
|
|
1007
|
+
----
|
|
1008
|
+
$ ukiryu definitions info inkscape
|
|
123
1009
|
|
|
124
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1028
|
+
✓ Definition installed successfully
|
|
1029
|
+
----
|
|
137
1030
|
|
|
138
|
-
|
|
1031
|
+
===== Remove Definition
|
|
139
1032
|
|
|
140
|
-
[source,
|
|
1033
|
+
[source,bash]
|
|
141
1034
|
----
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
1043
|
+
// CLI Reference
|
|
1044
|
+
== CLI Reference
|
|
147
1045
|
|
|
148
|
-
|
|
1046
|
+
=== List Available Tools
|
|
149
1047
|
|
|
150
|
-
[source,
|
|
1048
|
+
[source,bash]
|
|
151
1049
|
----
|
|
152
|
-
|
|
153
|
-
Ukiryu::Shell.detect # => :bash, :zsh, :powershell, etc.
|
|
1050
|
+
ukiryu list
|
|
154
1051
|
----
|
|
155
1052
|
|
|
156
|
-
===
|
|
1053
|
+
=== Get Tool Information
|
|
157
1054
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
[source,ruby]
|
|
1055
|
+
[source,bash]
|
|
161
1056
|
----
|
|
162
|
-
|
|
163
|
-
# Automatically selects 1.0.yaml or 0.92.yaml based on detected version
|
|
1057
|
+
ukiryu info inkscape
|
|
164
1058
|
----
|
|
165
1059
|
|
|
166
|
-
|
|
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
|
-
|
|
1066
|
+
=== Describe Commands
|
|
169
1067
|
|
|
170
|
-
[source,
|
|
1068
|
+
[source,bash]
|
|
171
1069
|
----
|
|
172
|
-
|
|
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
|
-
|
|
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
|
-
===
|
|
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
|
-
|
|
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
|
-
|
|
1097
|
+
Shows:
|
|
1098
|
+
* Available shells on your system
|
|
1099
|
+
* All supported shells
|
|
1100
|
+
* Platform detection
|
|
228
1101
|
|
|
229
|
-
|
|
1102
|
+
=== Manage Definitions
|
|
230
1103
|
|
|
231
|
-
|
|
1104
|
+
==== List All Definitions
|
|
1105
|
+
|
|
1106
|
+
[source,bash]
|
|
232
1107
|
----
|
|
233
|
-
|
|
1108
|
+
ukiryu definitions list
|
|
234
1109
|
----
|
|
235
1110
|
|
|
236
|
-
|
|
1111
|
+
Shows all discovered definitions with source type and path.
|
|
237
1112
|
|
|
238
|
-
|
|
1113
|
+
==== Show Definition Paths
|
|
1114
|
+
|
|
1115
|
+
[source,bash]
|
|
239
1116
|
----
|
|
240
|
-
|
|
1117
|
+
ukiryu definitions paths
|
|
241
1118
|
----
|
|
242
1119
|
|
|
243
|
-
|
|
1120
|
+
Shows all search paths in priority order.
|
|
244
1121
|
|
|
245
|
-
|
|
1122
|
+
==== Get Definition Information
|
|
1123
|
+
|
|
1124
|
+
[source,bash]
|
|
246
1125
|
----
|
|
247
|
-
|
|
1126
|
+
ukiryu definitions info inkscape
|
|
248
1127
|
----
|
|
249
1128
|
|
|
250
|
-
|
|
1129
|
+
Shows detailed information about a specific definition.
|
|
251
1130
|
|
|
252
|
-
|
|
1131
|
+
==== Add Definition
|
|
253
1132
|
|
|
254
|
-
|
|
1133
|
+
[source,bash]
|
|
1134
|
+
----
|
|
1135
|
+
ukiryu definitions add ./mytool-1.0.yaml
|
|
1136
|
+
----
|
|
255
1137
|
|
|
256
|
-
|
|
1138
|
+
Installs a definition file to the user definitions directory.
|
|
257
1139
|
|
|
258
|
-
|
|
1140
|
+
==== Remove Definition
|
|
259
1141
|
|
|
260
|
-
[source,
|
|
1142
|
+
[source,bash]
|
|
261
1143
|
----
|
|
262
|
-
ukiryu
|
|
1144
|
+
ukiryu definitions remove mytool
|
|
263
1145
|
----
|
|
264
1146
|
|
|
265
|
-
|
|
1147
|
+
Removes a user definition.
|
|
266
1148
|
|
|
267
|
-
|
|
1149
|
+
==== Validate Definition File
|
|
1150
|
+
|
|
1151
|
+
[source,bash]
|
|
268
1152
|
----
|
|
269
|
-
|
|
270
|
-
[✓] ghostscript v10.0.0
|
|
271
|
-
[✓] inkscape v1.3
|
|
272
|
-
[✗] imagemagick version unknown
|
|
1153
|
+
ukiryu validate --definition ./mytool.yaml
|
|
273
1154
|
----
|
|
274
1155
|
|
|
275
|
-
|
|
1156
|
+
Validates a definition file without installing it.
|
|
276
1157
|
|
|
277
|
-
|
|
1158
|
+
==== Initialize New Definition
|
|
278
1159
|
|
|
279
|
-
[source,
|
|
1160
|
+
[source,bash]
|
|
280
1161
|
----
|
|
281
|
-
ukiryu
|
|
1162
|
+
ukiryu init mytool
|
|
282
1163
|
----
|
|
283
1164
|
|
|
284
|
-
|
|
1165
|
+
Creates a new tool definition from a template.
|
|
285
1166
|
|
|
286
|
-
[source,
|
|
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
|
-
|
|
1172
|
+
Lists available template types (basic, cli_tool, converter).
|
|
294
1173
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
1174
|
+
[source,bash]
|
|
1175
|
+
----
|
|
1176
|
+
ukiryu init mytool --type cli_tool --output mytool.yaml
|
|
1177
|
+
----
|
|
299
1178
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
1179
|
+
Creates a CLI tool definition and saves to file.
|
|
1180
|
+
|
|
1181
|
+
// Developer Experience
|
|
1182
|
+
== Developer Experience
|
|
304
1183
|
|
|
305
|
-
|
|
306
|
-
default:
|
|
307
|
-
Platforms: macos, linux
|
|
308
|
-
Shells: bash, zsh, fish
|
|
309
|
-
Version: any
|
|
1184
|
+
=== Error Suggestions
|
|
310
1185
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
-
|
|
1192
|
+
✗ Validation failed
|
|
317
1193
|
|
|
318
|
-
|
|
1194
|
+
- Unknown field: 'platfoms'
|
|
1195
|
+
Did you mean 'platforms'?
|
|
319
1196
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
1204
|
+
=== Template Generation
|
|
326
1205
|
|
|
327
|
-
|
|
1206
|
+
Ukiryu includes templates for common tool types to help you get started quickly:
|
|
1207
|
+
|
|
1208
|
+
[source,bash]
|
|
328
1209
|
----
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
====
|
|
1220
|
+
==== Template Types
|
|
338
1221
|
|
|
339
|
-
|
|
1222
|
+
===== Basic Template
|
|
340
1223
|
|
|
341
|
-
|
|
1224
|
+
Minimal template with required fields only:
|
|
1225
|
+
|
|
1226
|
+
[source,yaml]
|
|
342
1227
|
----
|
|
343
|
-
|
|
344
|
-
|
|
1228
|
+
name: mytool
|
|
1229
|
+
version: "1.0"
|
|
1230
|
+
$schema: https://ukiryu.com/schema/1.2
|
|
345
1231
|
|
|
346
|
-
|
|
347
|
-
|
|
1232
|
+
profiles:
|
|
1233
|
+
- name: default
|
|
1234
|
+
platforms: [linux, macos]
|
|
1235
|
+
shells: [bash, zsh]
|
|
348
1236
|
----
|
|
349
1237
|
|
|
350
|
-
|
|
1238
|
+
===== CLI Tool Template
|
|
351
1239
|
|
|
352
|
-
|
|
1240
|
+
Standard CLI tool with common commands:
|
|
1241
|
+
|
|
1242
|
+
[source,yaml]
|
|
353
1243
|
----
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
1265
|
+
profiles:
|
|
1266
|
+
- name: default
|
|
1267
|
+
platforms: [linux, macos]
|
|
1268
|
+
shells: [bash, zsh]
|
|
1269
|
+
option_style: double_dash_equals
|
|
366
1270
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
Description: JPEG/MIFF/PNG compression level
|
|
1271
|
+
commands:
|
|
1272
|
+
- name: help
|
|
1273
|
+
description: Show help information
|
|
371
1274
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
Remove all profiles and text attributes from image
|
|
1275
|
+
- name: version
|
|
1276
|
+
description: Show version information
|
|
375
1277
|
----
|
|
376
1278
|
|
|
377
|
-
|
|
1279
|
+
===== Converter Template
|
|
378
1280
|
|
|
379
|
-
|
|
1281
|
+
File conversion tool with extensive options:
|
|
380
1282
|
|
|
381
|
-
[source,
|
|
1283
|
+
[source,yaml]
|
|
382
1284
|
----
|
|
383
|
-
|
|
384
|
-
|
|
1285
|
+
name: imagick
|
|
1286
|
+
version: "1.0"
|
|
1287
|
+
$schema: https://ukiryu.com/schema/1.2
|
|
385
1288
|
|
|
386
|
-
|
|
387
|
-
|
|
1289
|
+
display_name: Imagick
|
|
1290
|
+
description: File conversion tool for various formats
|
|
388
1291
|
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
|
|
393
|
-
|
|
1305
|
+
commands:
|
|
1306
|
+
- name: convert
|
|
1307
|
+
description: Convert files between formats
|
|
1308
|
+
use_env_vars: [headless]
|
|
394
1309
|
|
|
395
|
-
|
|
396
|
-
|
|
1310
|
+
arguments:
|
|
1311
|
+
- name: inputs
|
|
1312
|
+
type: file
|
|
1313
|
+
variadic: true
|
|
1314
|
+
min: 1
|
|
1315
|
+
description: Input file(s)
|
|
397
1316
|
|
|
398
|
-
|
|
399
|
-
|
|
1317
|
+
options:
|
|
1318
|
+
- name: output
|
|
1319
|
+
type: file
|
|
1320
|
+
cli: --output
|
|
1321
|
+
required: true
|
|
1322
|
+
description: Output file path
|
|
400
1323
|
|
|
401
|
-
|
|
402
|
-
|
|
1324
|
+
- name: format
|
|
1325
|
+
type: symbol
|
|
1326
|
+
cli: --format
|
|
1327
|
+
values: [png, jpg, pdf, svg]
|
|
1328
|
+
description: Output format
|
|
403
1329
|
----
|
|
404
1330
|
|
|
405
|
-
|
|
1331
|
+
==== Customizing Templates
|
|
406
1332
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1351
|
+
==== Interactive Mode (Opt-In)
|
|
417
1352
|
|
|
418
|
-
|
|
1353
|
+
When `highline` or `tty-prompt` gems are available, Ukiryu provides interactive prompts:
|
|
1354
|
+
|
|
1355
|
+
[source,bash]
|
|
419
1356
|
----
|
|
420
|
-
|
|
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
|
-
|
|
430
|
-
|
|
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
|
-
|
|
1365
|
+
Tool name: [mytool]:
|
|
434
1366
|
|
|
435
|
-
|
|
1367
|
+
Version: [1.0]: 2.0
|
|
436
1368
|
|
|
437
|
-
[
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
|
|
443
|
-
|
|
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
|
-
|
|
1379
|
+
To enable interactive prompts, add one of these gems to your Gemfile:
|
|
447
1380
|
|
|
448
|
-
[source,
|
|
1381
|
+
[source,ruby]
|
|
449
1382
|
----
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
Duration: 1.2s
|
|
1383
|
+
# For rich prompts (recommended)
|
|
1384
|
+
gem 'tty-prompt'
|
|
453
1385
|
|
|
454
|
-
|
|
1386
|
+
# Or use highline
|
|
1387
|
+
gem 'highline'
|
|
455
1388
|
----
|
|
456
1389
|
|
|
457
|
-
|
|
1390
|
+
// Advanced Features
|
|
1391
|
+
== Advanced Features
|
|
458
1392
|
|
|
459
|
-
|
|
1393
|
+
=== Version Constraints
|
|
460
1394
|
|
|
461
|
-
|
|
1395
|
+
Ukiryu supports semantic versioning constraints when selecting tool definitions:
|
|
1396
|
+
|
|
1397
|
+
[source,ruby]
|
|
462
1398
|
----
|
|
463
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1427
|
+
=== Tool Aliases
|
|
1428
|
+
|
|
1429
|
+
Create shortcuts for frequently used tools:
|
|
1430
|
+
|
|
1431
|
+
[source,bash]
|
|
469
1432
|
----
|
|
470
|
-
|
|
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
|
-
|
|
1443
|
+
List all aliases:
|
|
1444
|
+
|
|
1445
|
+
[source,bash]
|
|
1446
|
+
----
|
|
1447
|
+
$ ukiryu alias list
|
|
474
1448
|
|
|
475
|
-
|
|
1449
|
+
Tool Aliases:
|
|
1450
|
+
magick → imagemagick
|
|
1451
|
+
gs7 → ghostscript/7.0
|
|
1452
|
+
convert → imagemagick:convert
|
|
476
1453
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
3. `../register/` relative to current working directory (development)
|
|
1454
|
+
Total: 3 aliases
|
|
1455
|
+
----
|
|
480
1456
|
|
|
481
|
-
|
|
1457
|
+
Resolve an alias:
|
|
482
1458
|
|
|
483
|
-
[source,
|
|
1459
|
+
[source,bash]
|
|
484
1460
|
----
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
1471
|
+
Remove an alias:
|
|
490
1472
|
|
|
491
|
-
[source,
|
|
1473
|
+
[source,bash]
|
|
492
1474
|
----
|
|
493
|
-
ukiryu
|
|
494
|
-
ukiryu info inkscape -r /path/to/register
|
|
1475
|
+
ukiryu alias remove magick
|
|
495
1476
|
----
|
|
496
1477
|
|
|
497
|
-
|
|
1478
|
+
=== Definition Caching
|
|
498
1479
|
|
|
499
|
-
|
|
1480
|
+
Ukiryu automatically caches tool definitions for performance:
|
|
500
1481
|
|
|
501
|
-
[source,
|
|
1482
|
+
[source,bash]
|
|
502
1483
|
----
|
|
503
|
-
|
|
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
|
-
|
|
509
|
-
|
|
1487
|
+
Status: Active
|
|
1488
|
+
Entries: 5
|
|
1489
|
+
TTL: 300 seconds
|
|
1490
|
+
Refresh Strategy: lazy
|
|
510
1491
|
|
|
511
|
-
#
|
|
512
|
-
|
|
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
|
-
#
|
|
520
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1504
|
+
=== Definition Resolution
|
|
530
1505
|
|
|
531
|
-
|
|
1506
|
+
See which definition would be used for a tool:
|
|
1507
|
+
|
|
1508
|
+
[source,bash]
|
|
532
1509
|
----
|
|
533
|
-
|
|
534
|
-
result = tool.execute(:process, {
|
|
535
|
-
stdin: '{"name": "value"}',
|
|
536
|
-
filter: '.'
|
|
537
|
-
})
|
|
1510
|
+
$ ukiryu resolve inkscape
|
|
538
1511
|
|
|
539
|
-
|
|
540
|
-
result = tool.execute(:process, {
|
|
541
|
-
stdin: File.read('/path/to/data.json'),
|
|
542
|
-
filter: '.name'
|
|
543
|
-
})
|
|
1512
|
+
Resolution for: inkscape
|
|
544
1513
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1530
|
+
Resolve with version constraint:
|
|
1531
|
+
|
|
1532
|
+
[source,bash]
|
|
559
1533
|
----
|
|
560
|
-
|
|
1534
|
+
$ ukiryu resolve imagemagick ">= 7.0"
|
|
561
1535
|
|
|
562
|
-
|
|
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
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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
|
-
|
|
575
|
-
|
|
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
|
-
|
|
1547
|
+
Priority icons:
|
|
1548
|
+
* `★` - User definitions
|
|
1549
|
+
* `◆` - Bundled definitions
|
|
1550
|
+
* `■` - Local system definitions
|
|
1551
|
+
* `□` - System definitions
|
|
1552
|
+
* `△` - Register definitions
|
|
582
1553
|
|
|
583
|
-
|
|
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
|
-
|
|
1556
|
+
Definition composition allows building complex definitions from simpler ones:
|
|
595
1557
|
|
|
596
|
-
[source,
|
|
1558
|
+
[source,yaml]
|
|
597
1559
|
----
|
|
598
|
-
|
|
1560
|
+
name: mytool
|
|
1561
|
+
version: "2.0"
|
|
599
1562
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
-
|
|
608
|
-
== OOP API
|
|
1581
|
+
=== Shadowing and Override Behavior
|
|
609
1582
|
|
|
610
|
-
|
|
1583
|
+
When multiple definitions exist for the same tool, the highest priority definition is used:
|
|
611
1584
|
|
|
612
|
-
|
|
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
|
-
|
|
1591
|
+
To explicitly use a specific definition source:
|
|
615
1592
|
|
|
616
|
-
[source,
|
|
1593
|
+
[source,bash]
|
|
617
1594
|
----
|
|
618
|
-
#
|
|
619
|
-
|
|
1595
|
+
# Use only system definitions
|
|
1596
|
+
ukiryu exec --system inkscape export
|
|
620
1597
|
|
|
621
|
-
#
|
|
622
|
-
|
|
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
|
-
|
|
631
|
-
|
|
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
|
-
===
|
|
1605
|
+
=== Structured Responses
|
|
640
1606
|
|
|
641
|
-
|
|
1607
|
+
No more parsing stdout/stderr:
|
|
642
1608
|
|
|
643
1609
|
[source,ruby]
|
|
644
1610
|
----
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
665
|
-
|
|
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
|
-
#
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
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
|
-
===
|
|
1643
|
+
=== Platform Adaptation
|
|
676
1644
|
|
|
677
|
-
|
|
1645
|
+
Same code works everywhere:
|
|
678
1646
|
|
|
679
1647
|
[source,ruby]
|
|
680
1648
|
----
|
|
681
|
-
|
|
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
|
-
|
|
684
|
-
result.success? # => true
|
|
685
|
-
result.exit_code # => 0
|
|
1661
|
+
=== Version Management
|
|
686
1662
|
|
|
687
|
-
|
|
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
|
-
|
|
693
|
-
|
|
694
|
-
|
|
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
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
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
|
-
===
|
|
1685
|
+
=== Headless Mode
|
|
703
1686
|
|
|
704
|
-
|
|
1687
|
+
GUI applications run without windows on servers:
|
|
705
1688
|
|
|
706
1689
|
[source,ruby]
|
|
707
1690
|
----
|
|
708
|
-
#
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
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
|
-
|
|
720
|
-
result = action.run(action_options)
|
|
1700
|
+
Check headless support:
|
|
721
1701
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
===
|
|
1708
|
+
=== Error Handling
|
|
731
1709
|
|
|
732
|
-
|
|
1710
|
+
Structured exceptions instead of magic exit codes:
|
|
733
1711
|
|
|
734
1712
|
[source,ruby]
|
|
735
1713
|
----
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1744
|
+
// Ecosystem Tools
|
|
1745
|
+
== Ecosystem Tools
|
|
749
1746
|
|
|
750
|
-
|
|
1747
|
+
Ukiryu provides tools for validating, linting, and documenting tool definitions.
|
|
751
1748
|
|
|
752
|
-
|
|
753
|
-
|
|
1749
|
+
=== Definition Validation
|
|
1750
|
+
|
|
1751
|
+
Validate definitions against JSON Schema and structural rules:
|
|
1752
|
+
|
|
1753
|
+
[source,bash]
|
|
754
1754
|
----
|
|
755
|
-
|
|
1755
|
+
# Validate a single file
|
|
1756
|
+
ukiryu validate file path/to/tool.yaml
|
|
756
1757
|
|
|
757
|
-
|
|
1758
|
+
# Validate all definitions in register
|
|
1759
|
+
ukiryu validate all
|
|
758
1760
|
|
|
759
|
-
|
|
1761
|
+
# Validate with custom schema
|
|
1762
|
+
ukiryu validate file path/to/tool.yaml --schema path/to/schema.json
|
|
760
1763
|
|
|
761
|
-
|
|
762
|
-
|
|
1764
|
+
# Test executable against actual tool (smoke test)
|
|
1765
|
+
ukiryu validate file path/to/tool.yaml --executable
|
|
763
1766
|
|
|
764
|
-
|
|
765
|
-
|
|
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
|
-
|
|
771
|
-
|
|
1770
|
+
# JSON output for CI/CD
|
|
1771
|
+
ukiryu validate file path/to/tool.yaml --format json
|
|
772
1772
|
----
|
|
773
1773
|
|
|
774
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
780
|
-
|
|
781
|
-
|
|
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
|
-
|
|
793
|
-
|
|
1799
|
+
=== Definition Linting
|
|
1800
|
+
|
|
1801
|
+
Check definitions for best practices and potential issues:
|
|
1802
|
+
|
|
1803
|
+
[source,bash]
|
|
794
1804
|
----
|
|
795
|
-
|
|
1805
|
+
# Lint a definition file
|
|
1806
|
+
ukiryu lint file path/to/tool.yaml
|
|
796
1807
|
|
|
797
|
-
#
|
|
798
|
-
|
|
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
|
-
|
|
810
|
-
|
|
1811
|
+
# List all linting rules
|
|
1812
|
+
ukiryu lint rules
|
|
811
1813
|
----
|
|
812
1814
|
|
|
813
|
-
|
|
1815
|
+
Programmatic usage:
|
|
1816
|
+
|
|
814
1817
|
[source,ruby]
|
|
815
1818
|
----
|
|
816
|
-
|
|
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
|
-
#
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
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
|
-
|
|
1832
|
+
Linting rules include:
|
|
831
1833
|
|
|
832
|
-
|
|
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
|
-
|
|
1839
|
+
=== Documentation Generation
|
|
835
1840
|
|
|
836
|
-
|
|
1841
|
+
Generate human-readable documentation from definitions:
|
|
1842
|
+
|
|
1843
|
+
[source,bash]
|
|
837
1844
|
----
|
|
838
|
-
|
|
839
|
-
|
|
1845
|
+
# Generate docs for a tool
|
|
1846
|
+
ukiryu docs generate imagemagick
|
|
840
1847
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
pattern: "(\\d+\\.\\d+)"
|
|
1848
|
+
# Generate docs to file
|
|
1849
|
+
ukiryu docs generate imagemagick --output imagemagick.md
|
|
844
1850
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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
|
-
|
|
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
|
|
1934
|
+
* https://github.com/ukiryu/register[ukiryu/register] - Tool profile register
|
|
908
1935
|
* https://github.com/ukiryu/schema[ukiryu/schema] - Profile validation schema
|