hiiro 0.1.388 → 0.1.390

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75bc072edb723fca8202393111317406374a79d033efd727f5f2e6de708a1eca
4
- data.tar.gz: 220302e41947fce7f174a11040b363ae101c29ab0dc39ac8014302e076da36b2
3
+ metadata.gz: e9c16c581ea6c758abd518a2f706d980cd7e7d4f2c0efad6fb8f99de4ba8fcdb
4
+ data.tar.gz: 85fbe331a3e3ddf3fd36adfb3864d5f4d131a17714be759a65eaf79940f423ce
5
5
  SHA512:
6
- metadata.gz: e73489ef10468e4d7d95c1eb122ca5554c25e0ed2ea039497e1618a4c82130acdec2aa1458cbed31a55ad3be95d3bc7472bee53564aa9289df57e9d2902a87f2
7
- data.tar.gz: 8b76fd1f7b729ad8773a2fbffad8b6ab6eceaaa1a4cd7895517fe8ea28fed98c8a590e61b43d2ece451af86a29c316bc1f1c09d0580dfe2702758f23e6e20a9b
6
+ metadata.gz: ab5e23b6ebe2c5e0fbfb17000ce471c965efc3988777e346d15bbaeb54d1bc3ca1479f0ed21c98a52a0f959d1066f6c0c936a665e71ba29d3132c6f64c70062d
7
+ data.tar.gz: eb6a4353ae61c553c5f1cf524e18548161ff201a465cdf682ea04e897028b6b7df56ce356fd57259ddc7fd3951370b8ad65691453bbf6995c25a68869af2159b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.390] - 2026-10-07
4
+
5
+ ### Added
6
+ - `Hiiro::Tuios` class and `Hiiro#tuios` accessor for TUIOS integration
7
+
3
8
  ## [0.1.387.pre.2] - 2026-10-01
4
9
 
5
10
  ### Changed
data/README.md CHANGED
@@ -73,6 +73,49 @@ Saved files can be listed, printed, copied back to the clipboard, opened,
73
73
  edited, or removed with `h save ls|show|copy|open|edit|rm`. See
74
74
  [docs/h-save.md](docs/h-save.md).
75
75
 
76
+ ### Extract files, directories, and links
77
+
78
+ `h extract files`, `h extract dirs`, and `h extract links` read text from stdin
79
+ or input files and print one unique match per line, in first-encounter order.
80
+
81
+ ```sh
82
+ # Read text from stdin
83
+ printf '%s\n' 'README.md:12:3' 'README.md' | h extract files
84
+ printf '%s\n' 'docs/' './docs/' | h extract dirs
85
+ printf '%s\n' '(https://example.org/a_(b)).' | h extract links
86
+
87
+ # Read text from input files
88
+ h extract files build.log
89
+ h extract dirs build.log
90
+ h extract links notes.txt build.log
91
+ ```
92
+
93
+ Files and directories must exist and match the requested type. Relative paths
94
+ are checked against the current directory by default. Repeat `--base-dir DIR`
95
+ or `-b DIR` to replace that default with explicit bases. A match under any one
96
+ base is enough:
97
+
98
+ ```sh
99
+ h extract files --base-dir /path/to/project -b /path/to/other-project build.log
100
+ ```
101
+
102
+ Base directories affect path checks, not the locations of input files. Output
103
+ preserves the original path spelling rather than expanding or normalizing it.
104
+ Quotes and surrounding wrappers are removed, as are `:line` and `:line:column`
105
+ suffixes. Quoted paths can contain spaces, such as `"src/my file.rb:12:3"`.
106
+ Links must use HTTP or HTTPS. Trailing punctuation is removed, while balanced
107
+ parentheses in URLs are kept.
108
+
109
+ `--no-check` skips filesystem checks and uses text heuristics, not actual
110
+ existence or type. Files need a slash or a dotted filename and cannot end in a
111
+ slash. Directories need a slash and cannot have a dotted basename. These rules
112
+ can misclassify paths or omit real files and directories:
113
+
114
+ ```sh
115
+ printf '%s\n' '"src/my file.rb:12:3"' | h extract files --no-check
116
+ printf '%s\n' 'missing/output/' | h extract dirs --no-check
117
+ ```
118
+
76
119
  ### Task CLI
77
120
 
78
121
  `t` and `tt` are gem executables in `exe/`, installed alongside `h`. They are thin
@@ -167,6 +210,7 @@ See the [task command reference](docs/t.md) for all commands and the [workflow i
167
210
  | `h commit` | Select commits using fuzzy finder |
168
211
  | `h config` | Open config files (vim, git, Herdr, zsh, starship, claude) |
169
212
  | `h env` | Append safely quoted environment variables |
213
+ | [`h extract`](#extract-files-directories-and-links) | Extract file paths, directories, and HTTP(S) links from text |
170
214
  | `h link` | Manage saved links with URL, description, and shorthand |
171
215
  | `h pane` | Herdr pane management |
172
216
  | `h plugin` | Manage hiiro plugins (list, edit, search) |
data/bin/h-extract ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'hiiro'
4
+
5
+ options = Hiiro::Options.setup do
6
+ option(:base_dir, short: :b, multi: true, desc: 'Base directory for relative path checks (repeatable; default: cwd)')
7
+ flag(:no_check, desc: 'Extract path-shaped text without checking the filesystem')
8
+ end
9
+
10
+ clean_url = lambda do |url|
11
+ url = url.sub(/[.,;:!?]+\z/, '')
12
+ { ')' => '(', ']' => '[', '}' => '{' }.each do |closing, opening|
13
+ url = url.chop while url.end_with?(closing) && url.count(closing) > url.count(opening)
14
+ end
15
+ url.sub(/[.,;:!?]+\z/, '')
16
+ end
17
+
18
+ Hiiro.run(*ARGV) do
19
+ %i[files dirs links].each do |kind|
20
+ add_subcmd(kind) do |*args|
21
+ vals = options.parse!(args)
22
+ if vals.help?
23
+ puts "Usage: h extract #{kind} [options] [input-file ...]"
24
+ puts options.help_text
25
+ next
26
+ end
27
+
28
+ abort("--base-dir requires a directory") if vals.base_dir.any? { |base| base.nil? || base.empty? }
29
+ bases = vals.base_dir.empty? ? [Dir.pwd] : vals.base_dir.map { |base| File.expand_path(base) }
30
+ input = Hiiro::Shell.read_input(*vals.args)
31
+ matches = if kind == :links
32
+ input.scan(%r{https?://[^\s<>"'`]+}i).map { |url| clean_url.call(url) }
33
+ else
34
+ # Consume URLs whole so their slash-separated pieces cannot become paths.
35
+ tokens = input.scan(%r{https?://[^\s<>"'`]+|(?<![[:alnum:]])(?:"[^"\n]+"|'[^'\n]+'|`[^`\n]+`)|[^\s<>"'`()\[\]{},;=]+}i)
36
+ tokens.filter_map do |token|
37
+ quoted = ['"', "'", '`'].include?(token[0])
38
+ original = quoted ? token[1...-1] : token
39
+ path = original.sub(/[.!?:]+\z/, '').sub(/:\d+(?::\d+)?\z/, '')
40
+ next if path.empty? || path.match?(%r{\A[a-z][a-z0-9+.-]*://}i)
41
+
42
+ if vals.no_check
43
+ next unless path.include?('/') || path.match?(/\A[^\s]+\.[^\s.]+\z/)
44
+ next if kind == :files && path.end_with?('/')
45
+ next if kind == :dirs && File.basename(path).match?(/\.[^.]+\z/)
46
+ else
47
+ path = [original, path].uniq.find do |candidate|
48
+ bases.any? do |base|
49
+ full_path = File.expand_path(candidate, base)
50
+ kind == :files ? File.file?(full_path) : File.directory?(full_path)
51
+ end
52
+ end
53
+ next unless path
54
+ end
55
+ path
56
+ end
57
+ end
58
+ puts matches.uniq
59
+ end
60
+ end
61
+ end