hiiro 0.1.376 → 0.1.377

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: a8a343767837132ea50864926898caf5fcbc01788bbfadd351f426fe6e7bc770
4
- data.tar.gz: e8274d74d87414a087f45f3bf65584ecec9ffd999d48ca5328591e729babdf9e
3
+ metadata.gz: 6f85065ec7cb2c8b4fe08aba7d0692ef740963b272035324d57befbf471e9e23
4
+ data.tar.gz: 26745904bd4837c6b3fc2a4bf3372a2715c719af23c975b1dbcf7ad1445f430c
5
5
  SHA512:
6
- metadata.gz: 46b9f915a21b4c84a14971b5b30aa1a6f48f1507b049eb4ff59546e627c9eccb84cc0ac8863591dc50ab4f4ae4afff3af45c599c430751bcc3b1b7bb3e81fefc
7
- data.tar.gz: 45c777ea183fbc5eb26de72be4b7a4e303423c76f0ca99431b492d6615e82d296932cddb17c46f7f2ea6dabdd936e2762290d9a81375dc5d82413f35d770fabe
6
+ metadata.gz: bcee60b2999a7acddb1918f05184ba2f84055f59440bec14b323d7a328c685cbf076a66d8c8b9753c91c92720213169c7d14b705946cf934e7382966953e9462
7
+ data.tar.gz: 024bb62f15a76b36e01e102f76d2a505dd78f2db8079f19fce7d3c7e19bd3d892aac31ab45db24a5b1ecc36ef631982fa40a64b6ede114693bb7b5664f5ea8f4
data/.DS_Store CHANGED
Binary file
data/AGENTS.md ADDED
@@ -0,0 +1,544 @@
1
+ ## Project Overview
2
+
3
+ Hiiro is a lightweight CLI framework for Ruby that enables building multi-command tools similar to `git` or `docker`. It provides subcommand dispatch, abbreviation matching (e.g., `h ex hel` matches `h example hello`), and a plugin system.
4
+
5
+ ## Development Commands
6
+
7
+ ```bash
8
+ # Edit the main h script
9
+ h edit
10
+
11
+ # List available subcommands
12
+ h
13
+
14
+ # Syntax check Ruby files
15
+ ruby -c bin/h
16
+ ruby -c plugins/*.rb
17
+ ```
18
+
19
+
20
+ Tests are organized under `test/`:
21
+ - `test/hiiro/` - Core library tests (Matcher, Options, Shell, Fuzzyfind, Todo, etc.)
22
+ - `test/plugins/` - Plugin tests (Pins, Tasks, Notify, Project)
23
+ - `test/bin/` - Bin file tests using `Hiiro::TestHarness`
24
+
25
+ The `Hiiro::TestHarness` class (in `test/test_helper.rb`) enables testing bin files by capturing the block passed to `Hiiro.run` and evaluating it in a test context with stubbed `system` calls.
26
+
27
+ ## Architecture
28
+
29
+ ### Core Components (bin/h)
30
+
31
+ The `Hiiro` class is the main entry point with these nested classes:
32
+
33
+ - **`Runners`** - Discovers executables matching `h-*` in PATH and manages inline subcommands. Implements exact and prefix-based matching.
34
+ - **`Runners::Bin`** - Represents external executables found in PATH
35
+ - **`Runners::Subcommand`** - Represents inline subcommands registered via blocks
36
+ - **`Args`** - Parses single-dash flags (`-abc` becomes flags `a`, `b`, `c`)
37
+ - **`Config`** - Manages `~/.config/hiiro/` directory structure
38
+
39
+ ### Subcommand Resolution Flow
40
+
41
+ 1. `Hiiro.init()` parses first arg as subcommand name
42
+ 2. `Runners` searches for exact match in subcommands and PATH executables
43
+ 3. If no exact match, tries prefix matching (abbreviations)
44
+ 4. Ambiguous matches show help with possible options
45
+
46
+ ### Plugin System
47
+
48
+ Plugins are Ruby modules with a `self.load(hiiro)` method:
49
+
50
+ ```ruby
51
+ module MyPlugin
52
+ def self.load(hiiro)
53
+ # Add methods to hiiro instance
54
+ hiiro.instance_eval do
55
+ def my_helper; end
56
+ end
57
+
58
+ # Register subcommands
59
+ hiiro.add_subcmd(:mycmd) { |*args| ... }
60
+ end
61
+ end
62
+ ```
63
+
64
+ Plugins auto-load from `~/.config/hiiro/plugins/`. Load order matters when plugins depend on each other (e.g., Task and Project depend on Tmux).
65
+
66
+ ### Global Values Pattern
67
+
68
+ Values passed to `Hiiro.run()` are available in subcommand handlers:
69
+
70
+ ```ruby
71
+ Hiiro.run(*ARGV, cwd: Dir.pwd) do
72
+ add_subcmd(:pwd) { |*args|
73
+ cwd = get_value(:cwd)
74
+ puts cwd
75
+ }
76
+ end
77
+ ```
78
+
79
+ ## Creating Subcommands
80
+
81
+ There are two ways to add subcommands to Hiiro:
82
+
83
+ ### 1. External Bin File (Preferred)
84
+
85
+ Create a new executable `bin/h-<name>` that uses `Hiiro.run`:
86
+
87
+ ```ruby
88
+ #!/usr/bin/env ruby
89
+ require 'hiiro'
90
+
91
+ Hiiro.run(*ARGV, plugins: [:Tasks]) {
92
+ add_subcmd(:list) {
93
+ puts "Listing items..."
94
+ }
95
+
96
+ add_subcmd(:add) { |name, path=nil|
97
+ puts "Adding #{name} at #{path}"
98
+ }
99
+
100
+ add_subcmd(:remove) { |name=nil|
101
+ if name.nil?
102
+ puts "Usage: h mycommand remove <name>"
103
+ next
104
+ end
105
+ puts "Removed #{name}"
106
+ }
107
+ }
108
+ ```
109
+
110
+ This creates commands like `h mycommand list`, `h mycommand add foo /path`.
111
+
112
+ ### 2. Nested Subcommands via build_hiiro
113
+
114
+ For complex command hierarchies with shared state, create a nested Hiiro instance:
115
+
116
+ ```ruby
117
+ def self.add_subcommands(hiiro)
118
+ hiiro.add_subcmd(:task) do |*args|
119
+ tm = TaskManager.new(hiiro, scope: :task)
120
+ build_hiiro(hiiro, tm).run
121
+ end
122
+ end
123
+
124
+ def self.build_hiiro(parent_hiiro, tm)
125
+ bin_name = [parent_hiiro.bin, parent_hiiro.subcmd || ''].join('-')
126
+
127
+ Hiiro.run(bin_name:, args: parent_hiiro.args) do
128
+ add_subcmd(:list) { tm.list }
129
+ add_subcmd(:start) { |name| tm.start_task(name) }
130
+ add_subcmd(:switch) { |name=nil|
131
+ name ||= tm.select_task_interactive
132
+ tm.switch_to_task(tm.task_by_name(name))
133
+ }
134
+ end
135
+ end
136
+ ```
137
+
138
+ This pattern:
139
+ - Passes remaining args from parent to child via `args: parent_hiiro.args`
140
+ - Allows shared state (like `tm`) across all nested subcommands
141
+ - Creates commands like `h task list`, `h task start foo`
142
+
143
+ ## Library Components (lib/)
144
+
145
+ The `lib/hiiro.rb` file and `lib/hiiro/` directory contain the core framework classes:
146
+
147
+ ### Hiiro (lib/hiiro.rb)
148
+
149
+ Main entry point with class methods:
150
+ - `Hiiro.run(*ARGV, plugins: [...]) { ... }` - Initialize and run immediately
151
+ - `Hiiro.init(*ARGV, plugins: [...]) { ... }` - Initialize without running (returns hiiro instance). **NEVER USE THIS** without a good reason, always favor `Hiiro.run`
152
+
153
+ Instance methods available in subcommand blocks:
154
+ - `git` - Returns `Hiiro::Git` instance for git operations
155
+ - `fuzzyfind(lines)` - Interactive selection via skim
156
+ - `fuzzyfind_from_map(hash)` - Interactive selection returning mapped value
157
+ - `pins` - Key-value storage per command
158
+ - `todo_manager` - Todo item management
159
+ - `attach_method(name, &block)` - Add methods to hiiro instance dynamically
160
+ - `make_child(subcmd, *args)` - Create nested Hiiro for sub-subcommands (returns instance, must call `.run` manually)
161
+ - `run_child(subcmd, *args)` - Create a nested Hiiro instance AND immediately run it (preferred over `make_child(...).run`)
162
+
163
+ ### Hiiro::Matcher (lib/hiiro/matcher.rb)
164
+
165
+ Handles pattern matching for commands and items with prefix and substring matching:
166
+
167
+ ```ruby
168
+ matcher = Hiiro::Matcher.new(items, :name)
169
+
170
+ # Prefix matching - find items where name starts with pattern
171
+ result = matcher.by_prefix("pre")
172
+ result.match? # Any matches?
173
+ result.one? # Exactly one match?
174
+ result.ambiguous? # Multiple matches?
175
+ result.first&.item # Get first matching item
176
+ result.resolved&.item # Get exact or single match
177
+
178
+ # Substring matching - find items where name contains pattern anywhere
179
+ result = matcher.by_substring("abc")
180
+ result.matches.map(&:item) # All matching items
181
+
182
+ # Path-based matching for hierarchical names (e.g., "task/subtask")
183
+ result = matcher.resolve_path("t/s")
184
+
185
+ # Class methods for one-off matching
186
+ Hiiro::Matcher.by_prefix(items, "pre", key: :name)
187
+ Hiiro::Matcher.by_substring(items, "abc", key: :name)
188
+ ```
189
+
190
+ Note: `Hiiro::PrefixMatcher` is aliased to `Hiiro::Matcher` for backward compatibility.
191
+
192
+ ### Hiiro::Git (lib/hiiro/git.rb)
193
+
194
+ Git operations wrapper with submodules for branches, worktrees, remotes, and PRs:
195
+
196
+ ```ruby
197
+ git = hiiro.git
198
+ git.root # Repository root path
199
+ git.branch # Current branch name
200
+ git.branches # List all branches
201
+ git.worktrees # List all worktrees
202
+ git.add_worktree(path, branch:) # Create worktree
203
+ git.move_worktree(from, to) # Rename worktree
204
+ git.current_pr # Get current PR info
205
+ ```
206
+
207
+ ### Hiiro::DB (lib/hiiro/db.rb)
208
+
209
+ SQLite persistence layer backed by Sequel. All data is stored in `~/.config/hiiro/hiiro.db`.
210
+
211
+ **Setup:** `Hiiro::DB.setup!` is called at startup — creates any missing tables, then runs a one-time YAML→SQLite migration if the DB is new.
212
+
213
+ **Model registration:** Each Sequel model calls `Hiiro::DB.register(self)` so `setup!` can create its table:
214
+
215
+ ```ruby
216
+ class Hiiro::MyModel < Sequel::Model(:my_table)
217
+ Hiiro::DB.register(self)
218
+
219
+ def self.create_table!(db)
220
+ db.create_table?(:my_table) do
221
+ primary_key :id
222
+ String :name, null: false
223
+ end
224
+ end
225
+ end
226
+ ```
227
+
228
+ **Dual-write:** During rollout, models write to both SQLite and YAML. Once migration is stable, call `Hiiro::DB.disable_dual_write!` to stop YAML writes.
229
+
230
+ **Test isolation:** Set `ENV['HIIRO_TEST_DB'] = 'sqlite::memory:'` before requiring `hiiro` to get a clean in-memory DB per test run. Call `Hiiro::DB.setup!` after require.
231
+
232
+ **`h db` subcommand:** Inspect and manage the database:
233
+ - `h db status` — show connection info and migration state
234
+ - `h db tables` — list all tables with row counts
235
+ - `h db q <sql>` — run raw SQL and print results
236
+ - `h db migrate` — re-run YAML import (if not yet migrated)
237
+ - `h db restore` — restore YAML files from SQLite data
238
+
239
+ ### Hiiro::Fuzzyfind (lib/hiiro/fuzzyfind.rb)
240
+
241
+ Integration with `sk` (skim) or `fzf` fuzzy finders:
242
+
243
+ ```ruby
244
+ selected = Hiiro::Fuzzyfind.select(["option1", "option2", "option3"])
245
+ # Returns selected string or nil if cancelled
246
+
247
+ value = Hiiro::Fuzzyfind.map_select({ "Display 1" => "value1", "Display 2" => "value2" })
248
+ # Shows keys, returns corresponding value
249
+ ```
250
+
251
+ ### Hiiro::TodoManager (lib/hiiro/todo.rb)
252
+
253
+ Todo item management with task association:
254
+
255
+ ```ruby
256
+ tm = Hiiro::TodoManager.new
257
+ tm.add("Fix bug", tags: "urgent", task_info: { task_name: "feature" })
258
+ tm.start(0) # Mark item as started
259
+ tm.done(0) # Mark item as done
260
+ tm.active # Items not done/skipped
261
+ tm.filter_by_task("feature") # Items for specific task
262
+ ```
263
+
264
+ ### Invocation Tracking (lib/hiiro/invocation.rb)
265
+
266
+ Every CLI invocation is automatically recorded to SQLite via `Hiiro::Invocation` and `Hiiro::InvocationResolution`. This happens in `Hiiro.init` — no extra setup needed.
267
+
268
+ **Schema:**
269
+ - `Hiiro::Invocation` — records `bin_name`, `argv_json`, `cwd`, `invoked_at`
270
+ - `Hiiro::InvocationResolution` — linked to an invocation; records `resolved_name`, `resolution_type` (exact/prefix/abbreviated), `subcmd`
271
+
272
+ **Query recent invocations:**
273
+ ```ruby
274
+ Hiiro::Invocation.order(Sequel.desc(:invoked_at)).limit(20).each do |inv|
275
+ puts "#{inv.invoked_at} #{inv.bin_name} #{JSON.parse(inv.argv_json).join(' ')}"
276
+ end
277
+ ```
278
+
279
+ Or via `h db q`:
280
+ ```bash
281
+ h db q "SELECT bin_name, argv_json, invoked_at FROM invocations ORDER BY invoked_at DESC LIMIT 10"
282
+ ```
283
+
284
+ ### Hiiro::Shell (lib/hiiro/shell.rb)
285
+
286
+ Utility for piping content to external commands:
287
+
288
+ ```ruby
289
+ Hiiro::Shell.pipe("content", "pbcopy") # Pipe string to command
290
+ Hiiro::Shell.pipe_lines(["a", "b"], "command") # Join array with newlines and pipe
291
+ ```
292
+
293
+ ### Hiiro::Options (lib/hiiro/options.rb)
294
+
295
+ Argument parsing with flag and option support:
296
+
297
+ ```ruby
298
+ opts = Hiiro::Options.parse(args) do
299
+ option(:output, short: :o, desc: "Output file")
300
+ option(:verbose, short: :v, type: :flag, desc: "Verbose output")
301
+ end
302
+ opts.output # Value of --output or -o
303
+ opts.verbose # true if --verbose or -v was passed
304
+ opts.args # Remaining non-option arguments
305
+ ```
306
+
307
+ ### Hiiro::Notification (lib/hiiro/notification.rb)
308
+
309
+ macOS notification wrapper using terminal-notifier:
310
+
311
+ ```ruby
312
+ Hiiro::Notification.show(hiiro) # Show notification based on hiiro.args
313
+ # Supports: -m message, -t title, -l link, -c command, -s sound (macOS system sound name via terminal-notifier -sound; none for silent)
314
+ ```
315
+
316
+ ### Hiiro::Queue (lib/hiiro/queue.rb)
317
+
318
+ Task queue that pipes prompts to `claude` via tmux. Tasks are markdown files with optional YAML frontmatter (`task_name`, `tree_name`, `session_name`) that flow through statuses: `wip` -> `pending` -> `running` -> `done`/`failed`.
319
+
320
+ Subcommands (`h queue <subcmd>`):
321
+ - `ls`/`list` - List all tasks with status, elapsed time, and preview
322
+ - `status` - Detailed status with working directory info
323
+ - `add` - Create a new prompt (opens editor or accepts stdin/args, supports `-t <task>` flag)
324
+ - `wip` - Create/edit a work-in-progress prompt
325
+ - `ready` - Move wip task to pending
326
+ - `run [name]` - Launch pending task(s) in tmux windows
327
+ - `watch` - Continuously poll and launch pending tasks
328
+ - `attach [name]` - Switch to running task's tmux window (fuzzy select if no name)
329
+ - `kill [name]` - Kill running task's tmux window, move to failed
330
+ - `retry [name]` - Move failed/done task back to pending
331
+ - `clean` - Remove all done/failed task files
332
+ - `dir` - Print queue directory path
333
+
334
+ Config: `~/.config/hiiro/queue/{wip,pending,running,done,failed}/`
335
+
336
+ Key internals:
337
+ - `Queue::Prompt` - Parses frontmatter to resolve task/tree/session for working directory
338
+ - Tasks launch in tmux windows within the task's session (from frontmatter) or default `hq` session
339
+ - Launcher script runs `cat prompt | claude`, then moves files to done/failed based on exit code
340
+
341
+ ### Hiiro::ServiceManager (lib/hiiro/service_manager.rb)
342
+
343
+ Manages background development services with tmux integration, env file management, and service groups.
344
+
345
+ Subcommands (`h service <subcmd>`):
346
+ - `ls`/`list` - List all services with running status, port, and base_dir
347
+ - `start <name> [--use VAR=variation ...]` - Start a service or group; prepares env file first
348
+ - `stop <name>` - Stop a running service (sends C-c to tmux pane or runs stop command)
349
+ - `attach <name>` - Switch to service's tmux pane
350
+ - `open <name>` - Open service URL in browser
351
+ - `url <name>` / `port <name>` - Print service URL or port
352
+ - `status <name>` - Show detailed service info (pid, pane, task, started_at)
353
+ - `add` - Add new service via editor template
354
+ - `rm`/`remove <name>` - Remove a service
355
+ - `config` - Edit services.yml
356
+ - `groups` - List all service groups and their members
357
+ - `env <name>` - Show env_vars, their variation options, and base_env/env_file config
358
+
359
+ Service config (`~/.config/hiiro/services.yml`):
360
+ ```yaml
361
+ my-rails:
362
+ base_dir: apps/myapp
363
+ host: localhost
364
+ port: 3000
365
+ init: ["bundle install"]
366
+ start: bundle exec rails s -p 3000
367
+ stop: ""
368
+ cleanup: []
369
+ env_file: .env.development # destination in base_dir
370
+ base_env: my-rails.env # template in ~/.config/hiiro/env_templates/
371
+ env_vars:
372
+ GRAPHQL_URL:
373
+ variations:
374
+ local: http://localhost:4000/graphql
375
+ staging: https://graphql.staging.example.com/graphql
376
+ ```
377
+
378
+ Service group config (same file, distinguished by `services:` key):
379
+ ```yaml
380
+ my-stack:
381
+ services:
382
+ - name: my-rails
383
+ use:
384
+ GRAPHQL_URL: staging
385
+ - name: my-graphql
386
+ ```
387
+
388
+ Key internals:
389
+ - `prepare_env(svc_name, variation_overrides:)` - Copies base_env template from `~/.config/hiiro/env_templates/` to `base_dir/env_file`, then injects variation values
390
+ - `find_group(name)` / `start_group(name, ...)` - Detect and start service groups, applying per-member `use:` overrides
391
+ - Default variation is `local` when not specified
392
+ - State tracked in `~/.config/hiiro/services/running.yml`
393
+
394
+ ### Hiiro::RunnerTool (lib/hiiro/runner_tool.rb)
395
+
396
+ Run dev tools (linters, formatters, test suites) against changed files.
397
+
398
+ Subcommands (`h run [change_set] [tool_type] [file_group]`):
399
+ - Default (no subcmd) - Run matching tools with positional filters
400
+ - `ls` - List configured tools with type, group, extensions, and variations
401
+ - `add` - Add new tool via editor template
402
+ - `rm <name>` - Remove a tool
403
+ - `config` - Edit tools.yml
404
+
405
+ Arguments (positional, any order):
406
+ - **change_set**: `dirty` (default, git status), `branch` (diff from main), `all`
407
+ - **tool_type**: `lint`, `test`, `format`
408
+ - **file_type_group**: custom group identifier (e.g., `ruby`, `frontend`)
409
+ - `--variation`/`-v <name>` - Use a named tool variation
410
+
411
+ Config (`~/.config/hiiro/tools.yml`):
412
+ ```yaml
413
+ rubocop:
414
+ tool_type: lint
415
+ command: "rubocop [FILENAMES]"
416
+ file_type_group: ruby
417
+ file_extensions: "rb"
418
+ variations:
419
+ quick: "rubocop --only Style [FILENAMES]"
420
+ fix: "rubocop -A [FILENAMES]"
421
+ ```
422
+
423
+ `[FILENAMES]` is replaced with the space-joined list of matching files.
424
+
425
+ ### Hiiro::AppFiles (lib/hiiro/app_files.rb)
426
+
427
+ Track frequently-used files per application, open them together in your editor.
428
+
429
+ Subcommands (`h file <subcmd>`):
430
+ - `ls [app_name]` - List tracked files (all apps or specific app)
431
+ - `add <app> <file1> [file2 ...]` - Add files to an app's file list
432
+ - `rm <app> <file1> [file2 ...]` - Remove files
433
+ - `edit <app>` - Open all tracked files in editor (vim uses `-O` for vertical splits)
434
+
435
+ Config: `~/.config/hiiro/app_files.yml`
436
+
437
+ Files are resolved relative to the current task's tree root when an environment is available.
438
+
439
+ ### Jumplist (bin/h-jumplist)
440
+
441
+ Vim-style navigation history for tmux. Records pane/window/session changes and lets you jump backward/forward through your navigation history.
442
+
443
+ Subcommands (`h jumplist <subcmd>`):
444
+ - `setup` - Install tmux hooks and keybindings (`Ctrl-B` back, `Ctrl-F` forward)
445
+ - `record` - Record current position (called automatically by tmux hooks)
446
+ - `back` - Navigate to previous position
447
+ - `forward` - Navigate to next position
448
+ - `ls`/`list` - Show history with timestamps and current position marker
449
+ - `clear` - Clear history
450
+ - `path` - Print jumplist file path
451
+
452
+ Config: `~/.config/hiiro/jumplist/` (per-client entries and position files, max 50 entries)
453
+
454
+ Dead panes are automatically pruned. Duplicate consecutive entries are deduplicated. Forward history is truncated when navigating to a new location (like vim).
455
+
456
+ ### Using `run_child`
457
+
458
+ `run_child` is the instance-level equivalent of `Hiiro.run` — it creates a child Hiiro instance scoped to a subcommand and immediately dispatches it. Use it instead of `make_child(...).run`:
459
+
460
+ ```ruby
461
+ # Inside a subcommand handler or plugin:
462
+ hiiro.add_subcmd(:service) do |*args|
463
+ sm = ServiceManager.new
464
+ hiiro.run_child(:service) do |h|
465
+ h.add_subcmd(:list) { sm.list }
466
+ h.add_subcmd(:start) { |name| sm.start(name) }
467
+ end
468
+ end
469
+ ```
470
+
471
+ This is equivalent to `hiiro.make_child(:service) { ... }.run`, but cleaner and mirrors the `Hiiro.run` / `Hiiro.init` relationship.
472
+
473
+ ## Coding Rules and Conventions
474
+
475
+ ### `Hiiro.run` vs `h = Hiiro.init` then `h.run`
476
+
477
+ If the code sets up new classes to be used within the Hiiro set of subcommands
478
+ or options/flags, inside the bin that calls them, then use the `init` version.
479
+
480
+ Structure it like this:
481
+
482
+ ```ruby
483
+ h = Hiiro.init(*ARGV) do
484
+ # ...
485
+ end
486
+
487
+ # setup classes here
488
+
489
+ h.run
490
+ ```
491
+ If the bin uses classes that are already defined, then use `Hiiro.run`
492
+
493
+ ### Keep docs current
494
+
495
+ ALWAYS update `README.md` and any files in `docs/` or other markdown files that describe how to use hiiro, its bins, or how it works whenever you change behavior. Never let these go stale.
496
+
497
+ ## Key Files
498
+
499
+ - `exe/h` - Entry point that loads lib/hiiro.rb
500
+ - `exe/t`, `exe/tt` - Task CLI launchers over `Hiiro::TaskCli` (`lib/hiiro/task_cli.rb`)
501
+ - `bin/h-*` - External subcommands (tmux wrappers, git helpers, jumplist, etc.)
502
+ - `lib/hiiro.rb` - Main Hiiro class and Runners
503
+ - `lib/hiiro/*.rb` - Supporting classes (Git, Matcher, Fuzzyfind, Todo, Shell, Options, Notification, Tmux, Queue, ServiceManager, RunnerTool, AppFiles)
504
+
505
+ ### not used anymore
506
+ - `plugins/*.rb` - Reusable plugin modules (Pins, Project, Tasks, Notify)
507
+
508
+ ## External Dependencies
509
+
510
+ - Ruby with `pry` gem
511
+ - `tmux` for session/window/pane management
512
+ - `sk` (skim) or `fzf` for fuzzy finding
513
+ - `gh` CLI for GitHub operations (h-pr)
514
+ - `terminal-notifier` for macOS notifications (notify plugin)
515
+ - `claude` CLI for queue task execution
516
+
517
+ ## Configuration Locations
518
+
519
+ All config lives in `~/.config/hiiro/`:
520
+ - `plugins/` - Auto-loaded plugin files
521
+ - `pins/` - Per-command YAML key-value storage
522
+ - `tasks/` - Task metadata for worktree management
523
+ - `queue/` - Prompt queue (wip, pending, running, done, failed)
524
+ - `services/` - Service runtime state
525
+ - `jumplist/` - Per-client tmux navigation history
526
+ - `env_templates/` - Base .env template files for services
527
+ - `projects.yml` - Project directory aliases
528
+ - `apps.yml` - App directory mappings for task plugin
529
+ - `services.yml` - Service and service group definitions
530
+ - `tools.yml` - Runner tool definitions
531
+ - `app_files.yml` - Per-app tracked file lists
532
+
533
+
534
+ # Groups of files
535
+
536
+ ## tmux-related files
537
+
538
+ - bin/h-buffer
539
+ - bin/h-pane
540
+ - bin/h-window
541
+ - bin/h-session
542
+ - lib/hiiro/tmux.rb
543
+ - lib/hiiro/tmux/*
544
+
data/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased]
3
+ ## [0.1.377] - 2026-09-17
4
+
5
+ ### Changed
6
+ - `h save` redesigned with slug-based filenames (`<timestamp>-<slug>.txt` / `<timestamp>-image.png`), image handling via `pngpaste`, and environment-configurable destination via `HIIRO_SAVED_DIR`
7
+
8
+ ### Added
9
+ - `h save` subcommands: `ls`/`list`, `dir`, `show`/`cat`, `copy`, `open`, `edit`, `rm`/`remove` for managing saved files
10
+ - `t path`, `t cd`, `t start`, and `t switch` accept optional `APP` argument to select a configured relative directory beneath the task root
11
+ - APP resolves by exact name or unique case-sensitive prefix; fails when ambiguous or missing
4
12
 
5
13
  ## [0.1.376] - 2026-09-14
6
14