hiiro 0.1.366 → 0.1.368

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: 97e9a140ccea0c61c6efe48395bda803e10bec8c0a933a95efb4a949a8788cdf
4
- data.tar.gz: 24c45d70c07d018be4cca31368d91d55b8c8caa89d326e65803c512dcdf3b93e
3
+ metadata.gz: 975c627734daa28e7efa5c45802cd3f296ec3f338c7e2ad73835e22d219223e6
4
+ data.tar.gz: bcd42f9f3b2ca7bbaf33b67e1535aa4729d3701f353d313bfe3a7f64c27b3e19
5
5
  SHA512:
6
- metadata.gz: e1b139bc6a4286240311b0106413abed936ebef51771b267d4284ecff70783c5df7a4a0a7b368251e481c8f72ed0cc5d0bc094fe513cffa1563df08123fa65dc
7
- data.tar.gz: 37b32e60d486151dad01c08fdff82db9e9cff23506c8dad72636421b91e77c9e2e7a342b145770d86c615cc982035c4dcc80f3039ec3e457809e431f0bcc5aa2
6
+ metadata.gz: 22b19c645b77356da44007ab866f00f769ef875444b9ad5b4d00ae080ff5797b3b4cc3c500b65118ba3801f025c061f8ef01bb885aff7a63692c83c3b1ee09cc
7
+ data.tar.gz: aad14264c543596424e10c301401e618e13adbef8dd7e6088b9d1b6c3e9374a357040d2886843057c290f0dda65a6f6100992bfbf3bdd3d1049eb914984ea8c5
data/CHANGELOG.md CHANGED
@@ -2,6 +2,53 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.1.368] - 2026-09-14
6
+
7
+ ### Added
8
+ - `h herdr`: Herdr plugin exposing `t` through keybound popups for fuzzy task switching, todo capture, todo copying, task shell, and nvim access; `h herdr install` copies and links the plugin, `h herdr keys` prints keybindings
9
+
10
+ ### Changed
11
+ - Task CLI grammar converted to command-first: `t COMMAND [TASK] [ARGS...]` matching the legacy `h task` rule where the first positional word is the task when it names one (exact or unique prefix), otherwise the current task is used and the word stays in the payload
12
+
13
+ ### Removed
14
+ - `Hiiro::TaskScope` class; task resolution logic integrated into `Hiiro::TaskCli`
15
+
16
+ ## [0.1.367] - 2026-09-14
17
+
18
+ ### Added
19
+ - `t use TASK` (alias `pin`) saves the fallback task; `t current` now only prints
20
+ - `-t TASK` / `-f` task options on every `t` command that acts on a task
21
+ - `h herdr`: a Herdr plugin (`herdr-plugin/herdr-plugin.toml`) with actions `hiiro.switch`, `hiiro.todo-add`, `hiiro.todos`, `hiiro.shell`, and `hiiro.nvim` that open a popup running `t`; `h herdr install` copies and links it, `h herdr keys` prints keybindings
22
+ - `Hiiro::TestHarness` understands `add_cmd`, `add_option`, `add_flag`, and `opts`
23
+ - `t TASK todo show ID` and `tt TASK show ID` print one todo's text; `todo ls --plain` prints text only
24
+ - Add `h env add NAME VALUE` and `h alias add NAME COMMAND...` to append safely quoted shell definitions, preferring existing zsh module files and falling back to the root dotfiles. Use native Hiiro options, including `opts.global` and `--`.
25
+ - Add `h bin add NAME [COMMAND ...]` to generate executable `Hiiro.run` templates with optional empty `add_cmd` blocks. Serialize command names as Ruby symbols and refuse existing files or symlinks.
26
+ - `t ls` and `t list` root commands list tasks like bare `t`
27
+ - `Hiiro::Error`: `Hiiro#run` prints only `ERROR: message` for it, no backtrace
28
+ - `builtin_commands: false` option for `Hiiro.run`/`run_child` to skip the automatic `pry` and `edit` commands
29
+ - `Hiiro#open_default(target)` opens a path or URL with `open`/`xdg-open`
30
+ - `t` and `tt` are gem executables (`exe/t`, `exe/tt`) installed with Hiiro
31
+ - `t TASK tree new|rm|resume`, `t TASK tree`, `t TASK path`, `t TASK branch`, `t TASK sh`, and `t TASK cd` bring worktree and shell commands from `h task` into `t`; subtasks are `parent/child` names
32
+ - `Hiiro::CurrentTask`: one resolver for the current task (Herdr workspace, working directory, saved pin) used by `t` and by `Environment#task`
33
+ - `Hiiro::Git.repo_dir` so worktree commands work when `~/work/.git` is a gitfile pointing at the bare repo
34
+
35
+ ### Fixed
36
+ - `h task start` and worktree listing failed with "Not a directory" when `~/work/.git` is a gitfile; git now runs in the parent directory
37
+
38
+ ### Changed
39
+ - `t` grammar is now command-first, `t COMMAND [TASK] [ARGS...]`, matching the old `h task` rule: the first positional word is the task when it names one (exact or unique prefix), otherwise the current task is used and the word stays in the payload. `.` is the current task, `-` selects orphan todos. `tt ...` is `t todo ...`. `Hiiro::TaskScope` is removed
40
+ - `t todo add` no longer creates tasks; only `t new NAME` does
41
+ - Move the task CLI into `lib/hiiro/task_cli.rb` (`Hiiro::TaskCli`); `exe/t` and `exe/tt` are thin `Hiiro.run` launchers and `bin/t`, `bin/tt` are symlinks
42
+ - `t` requires `hiiro` like other bins instead of editing the load path; `task_scope` and `task_sessions` load with `hiiro`
43
+ - `t` errors print without a backtrace; task, scope, and session errors subclass `Hiiro::Error`
44
+ - `t` saves the current task through the `PinRecord` model and resolves resources, documents, tabs, and panes with `Hiiro::Matcher`, accepting unique prefixes and offering fuzzyfind when no reference is given
45
+ - `tt` runs the todo scope in-process instead of exec'ing `t`
46
+ - `h task` is now a symlink to `t` (`bin/h-task` -> `exe/t`); the inline `h task` and `h subtask` subcommands are gone, and `h task start|stop|resume|switch|sh|cd|path|branch|todo` map to `t` commands (see docs/h-task.md)
47
+ - `Environment#task` also matches the working directory against task homes, primary directories, and registered directories
48
+ - `TaskManager#start_task` uses the extracted `create_tree`
49
+ - Task listing prints aligned columns with the open todo count after each name, e.g. `prez (3)`, plus `next:`/`waiting:` text
50
+ - `ls` and `list` are now reserved root words in `t`; tasks with those exact names need a unique prefix
51
+
5
52
  ## [0.1.366] - 2026-09-14
6
53
 
7
54
  ### Changed
data/CLAUDE.md CHANGED
@@ -508,6 +508,7 @@ ALWAYS update `README.md` and any files in `docs/` or other markdown files that
508
508
  ## Key Files
509
509
 
510
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`)
511
512
  - `bin/h-*` - External subcommands (tmux wrappers, git helpers, jumplist, etc.)
512
513
  - `plugins/*.rb` - Reusable plugin modules (Pins, Project, Tasks, Notify)
513
514
  - `lib/hiiro.rb` - Main Hiiro class and Runners
data/README.md CHANGED
@@ -61,67 +61,56 @@ h ping
61
61
 
62
62
  ### Task CLI
63
63
 
64
- The repository's `bin/t` contains the task commands and helpers, using Hiiro's
65
- `add_cmd` DSL and existing task records. `~/bin/t` and `~/bin/tt` are symlinks to
66
- the repository launchers; installing the gem does not install them. `t NAME new`
67
- creates a task and its notes directory without creating a Git worktree.
68
-
69
- Use `t TASK COMMAND...`. Bare `t` lists every task, including done and archived
70
- tasks. `t TASK` shows a task. Only exact root `t help` displays generic usage and
71
- native scoped help without looking up a task. Other first words are task
72
- references, even `new`, `show`, `edit`, or `he`.
73
-
74
- Task references prefer an exact name, then a unique case-sensitive prefix.
75
- Ambiguous prefixes fail. `t NAME new` creates the exact name instead of resolving
76
- a prefix. An unknown name is otherwise an error, except that `t NAME todo add`
77
- can create the task with its first todo.
78
-
79
- Use `.` for the current task. Selection checks the calling Herdr workspace,
80
- then the current directory, then the saved task. Stale or ambiguous context is
81
- an error. `t TASK current` saves a named selection without focusing a terminal;
82
- `t . current` only prints it. `t TASK switch` and `t TASK workspace` focus or
83
- create the task workspace and save a named selection only after success.
84
- `--show` inspects without changing focus or the saved task.
64
+ `t` and `tt` are gem executables in `exe/`, installed alongside `h`. They are thin
65
+ `Hiiro.run` launchers over `Hiiro::TaskCli` in `lib/hiiro/task_cli.rb`, which uses
66
+ the `add_cmd` DSL and existing task records. `bin/t` and `bin/tt` are symlinks to
67
+ the `exe/` files for running from a checkout with `ruby -Ilib bin/t`. `t new NAME`
68
+ creates a task and its notes directory; `t tree new NAME` adds a Git worktree.
69
+
70
+ Use `t COMMAND [TASK] [ARGS...]`. Bare `t`, `t ls`, or `t list` lists every task,
71
+ including done and archived tasks, with the count of open todos after each name.
72
+ Commands that act on a task take it from the first positional argument when
73
+ that word names a task (exact or unique case-sensitive prefix); otherwise the
74
+ current task is used and the word stays in the payload, exactly like the old
75
+ `h task` commands. `-t TASK` forces a task, `-f` picks one with a fuzzy finder,
76
+ `.` is the current task, and `-` selects orphan todos. Ambiguous prefixes fail.
77
+
78
+ The current task is the calling Herdr workspace, then the current directory
79
+ inside a task home, code directory, worktree, or registered directory, then the
80
+ task saved with `t use TASK`. `t switch TASK` focuses or creates the task
81
+ workspace and also saves it. `t current` only prints.
85
82
 
86
83
  ```sh
87
- t investigation new
88
- t investigation next "Inspect the failing request"
89
- t investigation todo add Compare the retry settings
90
- tt investigation add Inspect --help output
91
- t investigation doc new findings
92
- t investigation doc open findings
93
- t investigation
94
- t investigation todo rm 42 # Use an item ID printed by show or todo list.
95
- t investigation current
96
- t investigation switch
97
- t investigation workspace --show
98
- t . next "Write the handoff"
84
+ t new investigation
85
+ t next investigation "Inspect the failing request"
86
+ t todo add investigation Compare the retry settings
87
+ tt add Inspect --help output # current task
88
+ t doc new investigation findings
89
+ t show investigation
90
+ t todo rm 42 # an ID printed by show or todo list
91
+ t use investigation
92
+ t switch investigation
93
+ t switch --show
94
+ t next . "Write the handoff"
95
+ t tree new investigation
99
96
  ```
100
97
 
101
- A task has one `next_action` and can have multiple independent todos. `t TASK`
102
- and `t TASK todo` print todos with their IDs, statuses, and text in ID order.
103
- `t TASK todo rm ID` deletes only that exact decimal ID in the selected scope.
104
- `tt TASK ...` delegates to `t TASK todo ...`; bare `tt` means `t . todo`, and
105
- `tt help` displays todo help. Use `t - todo` or `tt -` for orphan todos, including
106
- `add` and `rm`. `-` is not a task and is invalid outside todo commands.
98
+ A task has one `next_action` and can have multiple independent todos. `t show`
99
+ and `t todo` print todos with their IDs, statuses, and text in ID order.
100
+ `t todo show ID` prints one todo's text and `t todo rm ID` deletes it.
101
+ `tt ...` is `t todo ...`. Use `t todo -` and `tt add - TEXT` for orphan todos.
107
102
 
108
- Every argument after `todo add` is literal text, including flags and `--`,
109
- except that leading `add -h` or `add --help` displays help. Empty text fails
110
- without creating a task.
103
+ Every argument after the task in `todo add` is literal text, including flags and
104
+ `--`, except a leading `-h` or `--help`. Empty text fails. Only `t new NAME`
105
+ creates tasks.
111
106
 
112
107
  Todos share the existing `todos` table with `h todo`. `t` writes only to the
113
108
  database and does not rewrite `todo.yml`. Adding or removing a todo does not
114
109
  change the task's next action, status, or saved selection. Task completion and
115
- archival preserve todos; `h task` behavior is unchanged.
110
+ archival preserve todos. `h task` is a symlink to `t`, so both share one grammar.
116
111
 
117
- `t TASK omp`, `t TASK codex` or `cdx`, and `t TASK claude` or `cld` start fresh
112
+ `t omp [TASK]`, `t codex` or `cdx`, and `t claude` or `cld` start fresh
118
113
  native CLI sessions in new focused Herdr tabs. Claude always runs `claude`.
119
- Only a nonempty prefix of `resume` as the first tool argument changes mode.
120
- Bare `resume` focuses the unique running instance of that tool in the task
121
- workspace, or launches the native resume picker if none is running. Multiple
122
- running matches are an error. With an ID or any other arguments after `resume`,
123
- the command always opens a new tab and passes those arguments to the native CLI.
124
- All other arguments, including `--help`, are tool flags, not `t` flags.
125
114
 
126
115
  Tasks can share an existing directory, including a worktree. `t` does not change
127
116
  Git state or promise task-isolated persisted AI sessions in shared directories.
@@ -139,18 +128,21 @@ See the [task command reference](docs/t.md) for all commands and the [workflow i
139
128
  | `h setup` | Install plugins and subcommands to system paths |
140
129
  | `h edit` | Open the h script in your editor |
141
130
  | `h alert` | macOS desktop notifications via terminal-notifier |
142
- | `h task` | Task management across git worktrees, including existing external worktrees (via Tasks plugin) |
143
- | `h subtask` | Subtask management within tasks (via Tasks plugin) |
131
+ | `h herdr` | Herdr plugin for `t`: fuzzy task switcher, todo capture, todo copy, task shell and nvim popups (`h herdr install`) |
132
+ | `h task` | Same program as `t`: task records, todos, worktrees (`t NAME tree new`), and Herdr workspaces |
144
133
 
145
134
  ### External Subcommands
146
135
 
147
136
  | Command | Description |
148
137
  |---------|-------------|
138
+ | `h alias` | Append safely quoted zsh aliases |
149
139
  | `h app` | Manage app directories within tasks/projects |
140
+ | `h bin` | Create, list, and edit Hiiro executables |
150
141
  | `h branch` | Git branch management with fuzzy selection and copy |
151
142
  | `h claude` | Claude CLI wrapper with Herdr split support |
152
143
  | `h commit` | Select commits using fuzzy finder |
153
144
  | `h config` | Open config files (vim, git, Herdr, zsh, starship, claude) |
145
+ | `h env` | Append safely quoted environment variables |
154
146
  | `h link` | Manage saved links with URL, description, and shorthand |
155
147
  | `h pane` | Herdr pane management |
156
148
  | `h plugin` | Manage hiiro plugins (list, edit, search) |
@@ -168,6 +160,33 @@ See the [task command reference](docs/t.md) for all commands and the [workflow i
168
160
 
169
161
  `h claude agents|commands|skills -a` prints the absolute file path for each matching `.claude` tool, including `SKILL.md` for skills.
170
162
 
163
+ ### Shell helpers
164
+
165
+ ```sh
166
+ h env add exam ple
167
+ h alias add ll 'ls -al'
168
+ h alias add --global PAGER '| less'
169
+ h bin add scratch
170
+ h bin add josh list show
171
+ ```
172
+
173
+ `h env add NAME VALUE` appends a literal `export NAME="VALUE"` to
174
+ `~/.zshenv.d/vars.zsh` when that file exists, otherwise to `~/.zshenv`.
175
+ `h alias add NAME COMMAND...` similarly prefers `~/.zshrc.d/aliases.zsh`,
176
+ falling back to `~/.zshrc`. Existing contents are preserved. A single quoted
177
+ command string preserves shell syntax; separate command arguments retain their
178
+ argument boundaries. Use `--global` or `-g` for a global zsh alias.
179
+
180
+ These commands use native Hiiro option parsing and help. Use `--` before
181
+ flag-like data, for example `h env add LABEL -- --literal` or
182
+ `h alias add inspect -- tool --help`. Start a new shell or source the relevant
183
+ configuration file to apply additions.
184
+
185
+ `h bin add NAME [COMMAND ...]` creates an executable `~/bin/h-NAME` using
186
+ `Hiiro.run`. Without commands its block is empty; each command adds an empty
187
+ `add_cmd` block with a Ruby symbol. Existing files and symlinks are never
188
+ overwritten. See the [bin command reference](docs/h-bin.md).
189
+
171
190
  ## Abbreviations
172
191
 
173
192
  Any subcommand can be abbreviated as long as the prefix uniquely matches:
@@ -187,7 +206,7 @@ Plugins are Ruby modules loaded from `~/.config/hiiro/plugins/`:
187
206
  |--------|-------------|
188
207
  | Pins | Per-command YAML key-value storage |
189
208
  | Project | Project directory navigation with Herdr workspace management |
190
- | Tasks | Task lifecycle management across git worktrees with external-worktree registration and subtask support |
209
+ | Tasks | `Hiiro::TaskManager` worktree creation and current-task environment used by `t`, `h service`, `h run`, and `h file` |
191
210
  | Notify | Herdr desktop notifications |
192
211
 
193
212
  ## Adding Subcommands
@@ -294,6 +313,13 @@ shared with nested groups without consuming its name again.
294
313
  For a CLI that should dispatch only registered blocks, pass
295
314
  `external_commands: false` to `Hiiro.run` and its `run_child` or `make_child` calls. This
296
315
  prevents unrelated same-prefix executables on `PATH` from taking precedence.
316
+ Pass `builtin_commands: false` as well to skip the automatic `pry` and `edit`
317
+ commands, for CLIs whose first word is data rather than a command name.
318
+
319
+ Raise `Hiiro::Error` (or a subclass) for expected user-facing failures such as
320
+ bad arguments or missing records. `Hiiro#run` prints `ERROR: message` to stderr
321
+ and exits 1 without a backtrace; any other exception still prints its backtrace.
322
+ `open_default(target)` opens a path or URL with the OS handler (`open` or `xdg-open`).
297
323
 
298
324
  ## Writing Plugins
299
325
 
data/bin/h-alias ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'hiiro'
4
+
5
+ Hiiro.run do
6
+ add_flag :global, short: :g, desc: 'Create a global zsh alias'
7
+
8
+ add_cmd :add, args: ['NAME', 'COMMAND...'], opts: [:global] do
9
+ abort 'Usage: h alias add [--global|-g] NAME COMMAND...' unless opts.args.length >= 2
10
+ name, *command_args = opts.args
11
+ abort 'Invalid alias name' unless /\A[A-Za-z0-9_.][A-Za-z0-9_.-]*\z/.match?(name)
12
+ command = command_args.length == 1 ? command_args.first : Shellwords.join(command_args)
13
+ quoted = "'" + command.gsub("'") { "'\\''" } + "'"
14
+
15
+ home = ENV.fetch('HOME')
16
+ path = File.join(home, '.zshrc.d', 'aliases.zsh')
17
+ path = File.join(home, '.zshrc') unless File.file?(path)
18
+
19
+ File.open(path, 'a+', 0o600) do |file|
20
+ unless file.size.zero?
21
+ file.seek(-1, IO::SEEK_END)
22
+ file.write("\n") unless file.read(1) == "\n"
23
+ end
24
+ file.write("alias #{opts.global ? '-g ' : ''}#{name}=#{quoted}\n")
25
+ end
26
+ puts "Added alias to #{path}"
27
+ end
28
+ end
data/bin/h-bin CHANGED
@@ -3,6 +3,26 @@
3
3
  require 'hiiro'
4
4
 
5
5
  Hiiro.run do
6
+ add_cmd :add, args: ['NAME', 'COMMAND...'] do
7
+ name, *commands = opts.args
8
+ name = name&.delete_prefix('h-')
9
+ abort 'Use a command name containing letters, digits, underscores, or hyphens' unless name&.match?(/\A[A-Za-z0-9][A-Za-z0-9_-]*\z/)
10
+ abort 'Subcommand names cannot be empty' if commands.any?(&:empty?)
11
+
12
+ path = File.join(Dir.home, 'bin', "h-#{name}")
13
+ body = commands.map { |command| " add_cmd #{command.to_sym.inspect} do\n end" }.join("\n\n")
14
+ template = "#!/usr/bin/env ruby\n\nrequire 'hiiro'\n\nHiiro.run do\n"
15
+ template << "#{body}\n" unless body.empty?
16
+ template << "end\n"
17
+
18
+ FileUtils.mkdir_p(File.dirname(path))
19
+ File.open(path, File::WRONLY | File::CREAT | File::EXCL, 0o755) do |file|
20
+ file.write(template)
21
+ file.chmod(0o755)
22
+ end
23
+ puts path
24
+ end
25
+
6
26
  add_subcmd(:list) do |*subcmd_names|
7
27
  patterns = subcmd_names.flatten.compact.map { |sc|
8
28
  sc.sub(/^(?!h-)/, '{h-,}')
data/bin/h-env ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'hiiro'
4
+
5
+ Hiiro.run do
6
+ add_cmd :add, args: %i[NAME VALUE] do
7
+ abort 'Usage: h env add NAME VALUE' unless opts.args.length == 2
8
+ name, value = opts.args
9
+ abort 'Invalid environment variable name' unless /\A[A-Za-z_][A-Za-z0-9_]*\z/.match?(name)
10
+
11
+ home = ENV.fetch('HOME')
12
+ path = File.join(home, '.zshenv.d', 'vars.zsh')
13
+ path = File.join(home, '.zshenv') unless File.file?(path)
14
+ escaped = value.gsub(/["\\$`]/) { |char| "\\#{char}" }
15
+
16
+ File.open(path, 'a+', 0o600) do |file|
17
+ unless file.size.zero?
18
+ file.seek(-1, IO::SEEK_END)
19
+ file.write("\n") unless file.read(1) == "\n"
20
+ end
21
+ file.write("export #{name}=\"#{escaped}\"\n")
22
+ end
23
+ puts "Added environment variable to #{path}"
24
+ end
25
+ end
data/bin/h-herdr ADDED
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'hiiro'
4
+ require 'fileutils'
5
+
6
+ # Herdr plugin glue for the t task CLI. The plugin manifest lives in the gem's
7
+ # herdr-plugin/ directory; `h herdr install` copies it to ~/.config/hiiro/herdr-plugin
8
+ # and links it with `herdr plugin link`. Actions run headless behind keybindings and
9
+ # open the plugin popup; `h herdr popup` runs inside that popup with a real TTY.
10
+
11
+ PLUGIN_ID = 'hiiro'
12
+ ACTIONS = %w[switch todo-add todos shell nvim].freeze
13
+ INSTALL_DIR = Hiiro::Config.config_dir('herdr-plugin')
14
+ SOURCE_DIR = File.expand_path('../herdr-plugin', File.dirname(File.realpath(__FILE__)))
15
+ KEYS_SNIPPET = <<~TOML
16
+ # Hiiro task actions (h herdr keys)
17
+ [[keys.command]]
18
+ key = "prefix+t"
19
+ type = "plugin_action"
20
+ command = "hiiro.switch"
21
+ description = "switch to task"
22
+
23
+ [[keys.command]]
24
+ key = "prefix+shift+a"
25
+ type = "plugin_action"
26
+ command = "hiiro.todo-add"
27
+ description = "add todo"
28
+
29
+ [[keys.command]]
30
+ key = "prefix+shift+y"
31
+ type = "plugin_action"
32
+ command = "hiiro.todos"
33
+ description = "copy a todo"
34
+
35
+ [[keys.command]]
36
+ key = "prefix+shift+s"
37
+ type = "plugin_action"
38
+ command = "hiiro.shell"
39
+ description = "task shell popup"
40
+
41
+ [[keys.command]]
42
+ key = "prefix+shift+e"
43
+ type = "plugin_action"
44
+ command = "hiiro.nvim"
45
+ description = "task notes in nvim"
46
+ TOML
47
+
48
+ def herdr_bin
49
+ ENV['HERDR_BIN_PATH'] || 'herdr'
50
+ end
51
+
52
+ def source_dir
53
+ return SOURCE_DIR if File.exist?(File.join(SOURCE_DIR, 'herdr-plugin.toml'))
54
+ gem_root = Gem.loaded_specs['hiiro']&.full_gem_path
55
+ gem_root && File.join(gem_root, 'herdr-plugin')
56
+ end
57
+
58
+ Hiiro.run(*ARGV, external_commands: false) do
59
+ add_cmd(:install) do
60
+ no_args = opts.args.empty?
61
+ raise Hiiro::Error, 'h herdr install takes no arguments' unless no_args
62
+ src = source_dir
63
+ raise Hiiro::Error, 'Plugin manifest not found; reinstall the hiiro gem' unless src
64
+ FileUtils.mkdir_p(File.dirname(INSTALL_DIR))
65
+ FileUtils.rm_rf(INSTALL_DIR)
66
+ FileUtils.cp_r(src, INSTALL_DIR)
67
+ puts "Copied plugin to #{INSTALL_DIR}"
68
+ linked = system(herdr_bin, 'plugin', 'link', INSTALL_DIR)
69
+ puts(linked ? 'Linked plugin "hiiro" in Herdr' : 'Plugin already linked or link failed; run: herdr plugin list')
70
+ puts "\nAdd keybindings to ~/.config/herdr/config.toml (h herdr keys prints them), then run:"
71
+ puts ' herdr server reload-config'
72
+ end
73
+
74
+ add_cmd(:uninstall) do
75
+ system(herdr_bin, 'plugin', 'unlink', PLUGIN_ID)
76
+ FileUtils.rm_rf(INSTALL_DIR)
77
+ puts "Removed #{INSTALL_DIR}"
78
+ end
79
+
80
+ add_cmd(:keys) { puts KEYS_SNIPPET }
81
+
82
+ add_cmd(:actions) { ACTIONS.each { |name| puts "hiiro.#{name}" } }
83
+
84
+ # Headless: bound to a key, opens the popup with the chosen action.
85
+ add_cmd(:action, args: %i[name]) do
86
+ name = opts.args.first
87
+ raise Hiiro::Error, "Unknown action #{name}; one of #{ACTIONS.join(', ')}" unless ACTIONS.include?(name)
88
+ size = %w[shell nvim].include?(name) ? %w[--width 90% --height 90%] : []
89
+ system(herdr_bin, 'plugin', 'pane', 'open', '--plugin', PLUGIN_ID, '--entrypoint', 'popup',
90
+ '--env', "HIIRO_HERDR_ACTION=#{name}", *size, '--focus')
91
+ end
92
+
93
+ # Runs inside the popup pane (real TTY, HERDR_WORKSPACE_ID set by Herdr).
94
+ add_cmd(:popup) do
95
+ name = ENV['HIIRO_HERDR_ACTION'] || opts.args.first
96
+ raise Hiiro::Error, "Set HIIRO_HERDR_ACTION to one of #{ACTIONS.join(', ')}" unless ACTIONS.include?(name)
97
+ run_popup(name)
98
+ end
99
+
100
+ def tasks
101
+ Hiiro::TaskRecord.all_as_list.reject { |task| %w[done archived].include?(task.task_status) }
102
+ end
103
+
104
+ def current_task_name
105
+ Hiiro::CurrentTask.new(herdr: -> { herdr_client }, pin: true).resolve&.name
106
+ end
107
+
108
+ def pick_task(prompt_current: true)
109
+ current = prompt_current && current_task_name
110
+ names = tasks.map(&:name)
111
+ names = [current, *names.reject { |n| n == current }] if current
112
+ raise Hiiro::Error, 'No tasks; create one with t NAME new' if names.empty?
113
+ choice = fuzzyfind(names)
114
+ raise Hiiro::Error, 'No task selected' if choice.nil? || choice.strip.empty?
115
+ choice.strip
116
+ end
117
+
118
+ def pause(message)
119
+ $stderr.puts message
120
+ sleep 2
121
+ end
122
+
123
+ def run_popup(name)
124
+ case name
125
+ when 'switch'
126
+ task = pick_task(prompt_current: false)
127
+ system('t', 'switch', task) || pause("t switch #{task} failed")
128
+ when 'todo-add'
129
+ task = pick_task
130
+ print "Todo for #{task}: "
131
+ text = $stdin.gets.to_s.strip
132
+ raise Hiiro::Error, 'No todo text' if text.empty?
133
+ system('t', 'todo', 'add', '-t', task, text) || pause('todo add failed')
134
+ sleep 1
135
+ when 'todos'
136
+ task = current_task_name || pick_task
137
+ items = Hiiro::TodoItem.where(task_name: task, subtask_name: nil).exclude(status: %w[done skip]).order(:id).all
138
+ raise Hiiro::Error, "No open todos for #{task}" if items.empty?
139
+ text = fuzzyfind_from_map(items.to_h { |item| ["#{item.id} #{item.text}", item.text] })
140
+ raise Hiiro::Error, 'No todo selected' unless text
141
+ Hiiro::Shell.pipe(text, 'pbcopy')
142
+ puts "Copied: #{text}"
143
+ sleep 1
144
+ when 'shell'
145
+ exec('t', 'sh')
146
+ when 'nvim'
147
+ task = Hiiro::CurrentTask.new(herdr: -> { herdr_client }, pin: true).resolve!
148
+ FileUtils.mkdir_p(task.home)
149
+ Dir.chdir(task.home)
150
+ exec(ENV['EDITOR'] || 'nvim', '.')
151
+ end
152
+ rescue Hiiro::Error => e
153
+ pause("ERROR: #{e.message}")
154
+ exit 1
155
+ end
156
+ end
data/bin/h-task ADDED
@@ -0,0 +1 @@
1
+ ../exe/t
data/bin/t ADDED
@@ -0,0 +1 @@
1
+ ../exe/t
data/bin/tt ADDED
@@ -0,0 +1 @@
1
+ ../exe/tt
data/docs/h-bin.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # h-bin
2
2
 
3
- List and edit hiiro bin scripts (`h-*` executables found in PATH).
3
+ Create, list, and edit Hiiro bin scripts.
4
4
 
5
- `h bin` uses `Hiiro::Bins` to scan the current `PATH` for executable files.
5
+ `h bin add` creates executables in `~/bin`. The `list` and `edit` commands use `Hiiro::Bins` to scan the current `PATH`.
6
6
 
7
7
  ## Synopsis
8
8
 
@@ -14,9 +14,56 @@ h bin <subcommand> [names...]
14
14
 
15
15
  | Subcommand | Description |
16
16
  |------------|-------------|
17
+ | `add NAME [COMMAND ...]` | Create an executable template without overwriting an existing file |
17
18
  | `list [names]` | List matching bin files |
18
19
  | `edit [names]` | Open matching bin files in editor |
19
20
 
21
+ ### add
22
+
23
+ Create `~/bin/h-NAME`. An optional `h-` prefix is accepted. Executable names must
24
+ start with a letter or digit and contain only letters, digits, underscores, or
25
+ hyphens.
26
+
27
+ ```sh
28
+ h bin add scratch
29
+ ```
30
+
31
+ With no command arguments, the generated file contains:
32
+
33
+ ```ruby
34
+ #!/usr/bin/env ruby
35
+
36
+ require 'hiiro'
37
+
38
+ Hiiro.run do
39
+ end
40
+ ```
41
+
42
+ To include empty command blocks:
43
+
44
+ ```sh
45
+ h bin add josh list show
46
+ ```
47
+
48
+ ```ruby
49
+ #!/usr/bin/env ruby
50
+
51
+ require 'hiiro'
52
+
53
+ Hiiro.run do
54
+ add_cmd :list do
55
+ end
56
+
57
+ add_cmd :show do
58
+ end
59
+ end
60
+ ```
61
+
62
+ Command names are serialized as Ruby symbols, including quoted symbols for
63
+ names such as `hello-world`. Generated files are executable. Existing files
64
+ and symlinks are refused, and no editor or generated command is launched.
65
+ Native Hiiro options handle `--help` and the `--` end-of-options marker.
66
+
20
67
  ### edit
21
68
 
22
69
  Open matching `h-*` bin files in your editor. With no arguments, opens `h-bin` itself.
data/docs/h-herdr.md ADDED
@@ -0,0 +1,41 @@
1
+ # h herdr
2
+
3
+ Expose the `t` task CLI inside Herdr as keybound actions. Each action opens a session-modal popup that runs `h herdr popup`, so fuzzy pickers and editors get a real TTY without disturbing the tiled layout.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ h herdr install # copies herdr-plugin/ to ~/.config/hiiro/herdr-plugin and runs herdr plugin link
9
+ h herdr keys # prints [[keys.command]] entries to paste into ~/.config/herdr/config.toml
10
+ herdr server reload-config
11
+ ```
12
+
13
+ `h herdr uninstall` unlinks the plugin and removes the copied directory. Requires Herdr 0.7.4 or newer, and `sk` or `fzf` on `PATH`.
14
+
15
+ ## Actions
16
+
17
+ | Action | Default key | Popup behavior |
18
+ |---|---|---|
19
+ | `hiiro.switch` | `prefix+t` | Fuzzy-pick an active or waiting task, then `t NAME switch` |
20
+ | `hiiro.todo-add` | `prefix+shift+a` | Fuzzy-pick a task (current task first), type the todo text, then `t NAME todo add TEXT` |
21
+ | `hiiro.todos` | `prefix+shift+y` | Fuzzy-pick an open todo of the current task and copy its text to the clipboard |
22
+ | `hiiro.shell` | `prefix+shift+s` | 90% popup running `t sh` in the current task's start directory |
23
+ | `hiiro.nvim` | `prefix+shift+e` | 90% popup running `$EDITOR` (default `nvim`) in the current task's notes home |
24
+
25
+ The current task resolves through `Hiiro::CurrentTask`: the popup's Herdr workspace, then the working directory, then the task saved with `t use TASK`. Errors print in the popup for two seconds before it closes.
26
+
27
+ ## Subcommands
28
+
29
+ | Command | Behavior |
30
+ |---|---|
31
+ | `h herdr install` | Copy the manifest from the gem, link it in Herdr, print next steps |
32
+ | `h herdr uninstall` | Unlink and remove the copy |
33
+ | `h herdr keys` | Print the keybinding snippet |
34
+ | `h herdr actions` | List action ids |
35
+ | `h herdr action NAME` | Headless: open the popup for NAME (what the manifest binds) |
36
+ | `h herdr popup` | Runs inside the popup; reads `HIIRO_HERDR_ACTION` |
37
+
38
+ ## Files
39
+
40
+ - `herdr-plugin/herdr-plugin.toml` in the gem: manifest with five `[[actions]]` and one `[[panes]]` popup entrypoint
41
+ - `bin/h-herdr`: actions, popup logic, install and keys helpers