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