hiiro 0.1.383 → 0.1.385

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.
Files changed (318) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -13
  3. data/README.md +1 -1
  4. data/bin/h-herdr +126 -0
  5. data/bin/h-js +21 -0
  6. data/bin/h-plist +214 -0
  7. data/bin/hh +4 -0
  8. data/doc/AGENTS_md.html +898 -0
  9. data/doc/AppTest/MockApp.html +192 -0
  10. data/doc/AppTest/MockEnvironment.html +192 -0
  11. data/doc/AppTest/MockGit.html +157 -0
  12. data/doc/AppTest/MockItem.html +178 -0
  13. data/doc/AppTest/MockMatcher.html +200 -0
  14. data/doc/AppTest/MockResult.html +235 -0
  15. data/doc/AppTest.html +401 -0
  16. data/doc/Asarfile/Appfile.html +4219 -0
  17. data/doc/Asarfile.html +1445 -0
  18. data/doc/BackgroundTest.html +388 -0
  19. data/doc/BinsTest.html +245 -0
  20. data/doc/BranchManager.html +373 -804
  21. data/doc/BranchTest/MockGit.html +272 -0
  22. data/doc/BranchTest.html +621 -0
  23. data/doc/CHANGELOG_md.html +771 -0
  24. data/doc/CLAUDE_md.html +310 -1092
  25. data/doc/ClaudeTest.html +323 -0
  26. data/doc/CommitTest.html +271 -0
  27. data/doc/ConfigTest.html +407 -0
  28. data/doc/ContextBundleManager.html +811 -0
  29. data/doc/CurrentTaskTest.html +325 -0
  30. data/doc/Directory.html +63 -770
  31. data/doc/EnvFile.html +397 -0
  32. data/doc/EnvironmentTest.html +610 -0
  33. data/doc/FilesystemEffectsTest.html +478 -0
  34. data/doc/FuzzyfindTest.html +382 -0
  35. data/doc/Gemfile.html +309 -669
  36. data/doc/Gemfile_lock.html +336 -691
  37. data/doc/GitBranchTest.html +428 -0
  38. data/doc/GitBranchesTest.html +428 -0
  39. data/doc/GitEffectsTest.html +381 -0
  40. data/doc/GitPrTest.html +581 -0
  41. data/doc/GitRemoteTest.html +519 -0
  42. data/doc/GitWorktreeTest.html +576 -0
  43. data/doc/GitWorktreesTest.html +712 -0
  44. data/doc/Globber.html +593 -0
  45. data/doc/HerdrEffectsTest.html +464 -0
  46. data/doc/HerdrPluginTest.html +344 -0
  47. data/doc/HerdrWorkspaceTest.html +230 -0
  48. data/doc/Hiiro/AnyStruct.html +78 -835
  49. data/doc/Hiiro/AppFiles.html +88 -882
  50. data/doc/Hiiro/AppRecord.html +233 -0
  51. data/doc/Hiiro/Args.html +82 -855
  52. data/doc/Hiiro/Assignment.html +232 -0
  53. data/doc/Hiiro/Background.html +287 -0
  54. data/doc/Hiiro/Bins.html +70 -818
  55. data/doc/Hiiro/Branch.html +416 -0
  56. data/doc/Hiiro/Capture.html +314 -0
  57. data/doc/Hiiro/CheckRun.html +261 -0
  58. data/doc/Hiiro/Config.html +178 -830
  59. data/doc/Hiiro/CurrentTask.html +254 -0
  60. data/doc/Hiiro/DB/JSON.html +200 -0
  61. data/doc/Hiiro/DB.html +440 -0
  62. data/doc/Hiiro/Effects/Executor/Filesystem/NullFilesystem.html +642 -0
  63. data/doc/Hiiro/Effects/Executor/Filesystem.html +750 -0
  64. data/doc/Hiiro/Effects/Executor/NullExecutor.html +436 -0
  65. data/doc/Hiiro/Effects/Executor.html +509 -0
  66. data/doc/Hiiro/Effects.html +121 -0
  67. data/doc/Hiiro/Error.html +127 -0
  68. data/doc/Hiiro/FallbackTarget/App.html +308 -0
  69. data/doc/Hiiro/FallbackTarget/Environment.html +777 -0
  70. data/doc/Hiiro/FallbackTarget.html +1748 -0
  71. data/doc/Hiiro/Fuzzyfind.html +70 -818
  72. data/doc/Hiiro/Git/Branch.html +106 -975
  73. data/doc/Hiiro/Git/Branches.html +128 -1084
  74. data/doc/Hiiro/Git/Pr.html +2575 -1115
  75. data/doc/Hiiro/Git/Remote.html +106 -971
  76. data/doc/Hiiro/Git/Worktree.html +90 -899
  77. data/doc/Hiiro/Git/Worktrees.html +129 -1085
  78. data/doc/Hiiro/Git.html +306 -1413
  79. data/doc/Hiiro/Glob.html +58 -757
  80. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces/Tab/Tabs/Pane/Panes.html +5289 -0
  81. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces/Tab/Tabs/Pane.html +5470 -0
  82. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces/Tab/Tabs.html +2292 -0
  83. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces/Tab.html +2619 -0
  84. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces.html +1339 -0
  85. data/doc/Hiiro/Herdr/Collection/Workspace.html +2337 -0
  86. data/doc/Hiiro/Herdr/Collection.html +2190 -0
  87. data/doc/Hiiro/Herdr.html +125 -0
  88. data/doc/Hiiro/InputFile.html +550 -0
  89. data/doc/Hiiro/Invocation.html +356 -0
  90. data/doc/Hiiro/InvocationResolution.html +218 -0
  91. data/doc/Hiiro/Link.html +507 -0
  92. data/doc/Hiiro/Matcher/Item.html +60 -777
  93. data/doc/Hiiro/Matcher/PathResult.html +106 -979
  94. data/doc/Hiiro/Matcher/Result.html +110 -1001
  95. data/doc/Hiiro/Matcher.html +147 -1138
  96. data/doc/Hiiro/Notification.html +159 -953
  97. data/doc/Hiiro/Options/Args/Definition.html +557 -0
  98. data/doc/Hiiro/Options/Args.html +1301 -1020
  99. data/doc/Hiiro/Options.html +155 -963
  100. data/doc/Hiiro/PaneHome.html +312 -0
  101. data/doc/Hiiro/Paths/Symlink.html +102 -946
  102. data/doc/Hiiro/Paths.html +75 -838
  103. data/doc/Hiiro/PinRecord.html +309 -0
  104. data/doc/Hiiro/PinnedPRManager.html +1388 -0
  105. data/doc/Hiiro/PinnedPr.html +2261 -0
  106. data/doc/Hiiro/Project.html +233 -0
  107. data/doc/Hiiro/Projects.html +437 -0
  108. data/doc/Hiiro/PsProcess.html +770 -0
  109. data/doc/Hiiro/Queue/Prompt.html +177 -927
  110. data/doc/Hiiro/Queue.html +963 -1622
  111. data/doc/Hiiro/Rbenv.html +197 -988
  112. data/doc/Hiiro/RegistryEntry/Hiiro.html +176 -0
  113. data/doc/Hiiro/RegistryEntry.html +842 -0
  114. data/doc/Hiiro/Reminder.html +238 -0
  115. data/doc/Hiiro/Result.html +462 -0
  116. data/doc/Hiiro/RunnerTool.html +106 -956
  117. data/doc/Hiiro/Runners/Bin.html +98 -939
  118. data/doc/Hiiro/Runners/Subcommand.html +100 -956
  119. data/doc/Hiiro/Runners.html +150 -1043
  120. data/doc/Hiiro/ServiceManager.html +251 -1283
  121. data/doc/Hiiro/Shell.html +226 -772
  122. data/doc/Hiiro/ShellCommand.html +277 -0
  123. data/doc/Hiiro/SparseGroups.html +64 -784
  124. data/doc/Hiiro/Tag.html +543 -0
  125. data/doc/Hiiro/Tags.html +117 -906
  126. data/doc/Hiiro/Task.html +203 -982
  127. data/doc/Hiiro/TaskCli/Commands/Error.html +137 -0
  128. data/doc/Hiiro/TaskCli/Commands.html +3128 -0
  129. data/doc/Hiiro/TaskCli.html +220 -0
  130. data/doc/Hiiro/TaskManager/Config.html +110 -876
  131. data/doc/Hiiro/TaskManager.html +530 -1372
  132. data/doc/Hiiro/TaskRecord.html +511 -0
  133. data/doc/Hiiro/TaskResource.html +176 -0
  134. data/doc/Hiiro/TaskSessions/Error.html +131 -0
  135. data/doc/Hiiro/TaskSessions.html +247 -0
  136. data/doc/Hiiro/Tasks.html +289 -776
  137. data/doc/Hiiro/TestHarness.html +1105 -0
  138. data/doc/Hiiro/TodoItem.html +184 -1165
  139. data/doc/Hiiro/TodoManager.html +163 -1185
  140. data/doc/Hiiro/Tree.html +123 -898
  141. data/doc/Hiiro/Tui/ListScreen.html +672 -0
  142. data/doc/Hiiro/Tui/Terminal.html +488 -0
  143. data/doc/Hiiro/Tui.html +121 -0
  144. data/doc/Hiiro.html +631 -1851
  145. data/doc/HiiroAddCmdTest.html +366 -0
  146. data/doc/HiiroAddDefaultTest.html +384 -0
  147. data/doc/HiiroRunnersBinTest.html +363 -0
  148. data/doc/HiiroRunnersSubcommandTest.html +430 -0
  149. data/doc/HiiroSetup.html +190 -0
  150. data/doc/HiiroTest.html +533 -0
  151. data/doc/HomeDirs.html +528 -0
  152. data/doc/JsTest.html +187 -0
  153. data/doc/LICENSE.html +309 -669
  154. data/doc/LinkManager/Link.html +86 -885
  155. data/doc/LinkManager.html +224 -1007
  156. data/doc/LinkTest/MockLinkManager.html +331 -0
  157. data/doc/LinkTest.html +198 -0
  158. data/doc/MatcherItemTest.html +167 -0
  159. data/doc/MatcherResultTest.html +375 -0
  160. data/doc/MatcherTest.html +908 -0
  161. data/doc/MockHiiro.html +346 -0
  162. data/doc/NotificationTest/MockHiiroForNotify.html +180 -0
  163. data/doc/NotificationTest.html +592 -0
  164. data/doc/Notify.html +77 -828
  165. data/doc/NotifyPluginTest.html +421 -0
  166. data/doc/Object.html +7684 -1547
  167. data/doc/ObsidianNotes.html +599 -0
  168. data/doc/OptionsDefinitionTest.html +348 -0
  169. data/doc/OptionsTest.html +1047 -0
  170. data/doc/PaneTest/MockHerdr.html +1113 -0
  171. data/doc/PaneTest.html +415 -0
  172. data/doc/Pins/Pin.html +355 -1062
  173. data/doc/Pins.html +73 -815
  174. data/doc/PinsPinTest/MockHiiro.html +178 -0
  175. data/doc/PinsPinTest.html +514 -0
  176. data/doc/PluginTest.html +388 -0
  177. data/doc/PortfolioApi.html +311 -0
  178. data/doc/Project.html +84 -941
  179. data/doc/ProjectPluginTest.html +507 -0
  180. data/doc/ProjectTest.html +249 -0
  181. data/doc/QueueHerdrTest.html +181 -0
  182. data/doc/README_md.html +557 -741
  183. data/doc/Rakefile.html +309 -669
  184. data/doc/ReminderManager/Reminder.html +626 -0
  185. data/doc/ReminderManager.html +469 -0
  186. data/doc/RequirePathsTest.html +168 -0
  187. data/doc/RubyGems/Version/Git/Claude.html +251 -0
  188. data/doc/RubyGems/Version/Git/RubyGem.html +214 -0
  189. data/doc/RubyGems/Version/Git.html +1175 -0
  190. data/doc/RubyGems/Version.html +379 -0
  191. data/doc/RubyGems.html +251 -0
  192. data/doc/SaveTest.html +352 -0
  193. data/doc/ServiceManagerHerdrTest.html +187 -0
  194. data/doc/SessionTest/MockHerdr.html +699 -0
  195. data/doc/SessionTest.html +343 -0
  196. data/doc/ShaTest.html +343 -0
  197. data/doc/ShellTest.html +430 -0
  198. data/doc/String.html +58 -757
  199. data/doc/SystemCallCapture.html +267 -0
  200. data/doc/TODO_md.html +311 -672
  201. data/doc/TaskCommandTest.html +2416 -0
  202. data/doc/TaskManager.html +1554 -1599
  203. data/doc/TaskManagerConfigTest.html +451 -0
  204. data/doc/TaskManagerFilterTasksTest.html +421 -0
  205. data/doc/TaskManagerFromWorktreeTest.html +308 -0
  206. data/doc/TaskTest.html +545 -0
  207. data/doc/TaskToggleTui.html +224 -0
  208. data/doc/TestHarnessEffectsTest.html +562 -0
  209. data/doc/TestHelpers.html +231 -0
  210. data/doc/TodoItemTest.html +983 -0
  211. data/doc/TodoManagerTest.html +1172 -0
  212. data/doc/TodoTest/MockTodoManager.html +398 -0
  213. data/doc/TodoTest.html +243 -0
  214. data/doc/TreeTest.html +477 -0
  215. data/doc/WindowTest/MockHerdr.html +746 -0
  216. data/doc/WindowTest.html +343 -0
  217. data/doc/WtreeTest.html +566 -0
  218. data/doc/bin/h-_config.html +362 -0
  219. data/doc/bin/h-dsstore-read.html +358 -0
  220. data/doc/css/fonts.css +167 -0
  221. data/doc/css/rdoc.css +328 -1600
  222. data/doc/demo_sh.html +314 -674
  223. data/doc/docs/README_md.html +477 -736
  224. data/doc/docs/coupling-and-extraction-plan_txt.html +1589 -0
  225. data/doc/docs/docker-testing-guide_md.html +668 -865
  226. data/doc/docs/h-app_md.html +615 -0
  227. data/doc/docs/h-bg_md.html +420 -0
  228. data/doc/docs/h-bin_md.html +475 -0
  229. data/doc/docs/h-branch_md.html +1035 -0
  230. data/doc/docs/h-claude_md.html +1121 -0
  231. data/doc/docs/h-commit_md.html +417 -0
  232. data/doc/docs/h-config_md.html +522 -0
  233. data/doc/docs/h-cpr_md.html +399 -0
  234. data/doc/docs/h-db_md.html +555 -0
  235. data/doc/docs/h-file_md.html +422 -0
  236. data/doc/docs/h-herdr_md.html +470 -0
  237. data/doc/docs/h-img_md.html +430 -0
  238. data/doc/docs/h-link_md.html +619 -0
  239. data/doc/docs/h-misc_md.html +435 -0
  240. data/doc/docs/h-notify_md.html +510 -0
  241. data/doc/docs/h-pane_md.html +405 -784
  242. data/doc/docs/h-plugin_md.html +382 -705
  243. data/doc/docs/h-pm_md.html +549 -0
  244. data/doc/docs/h-pr-monitor_md.html +458 -0
  245. data/doc/docs/h-pr_md.html +795 -0
  246. data/doc/docs/h-project_md.html +522 -0
  247. data/doc/docs/h-queue_md.html +744 -0
  248. data/doc/docs/h-registry_md.html +542 -0
  249. data/doc/docs/h-run_md.html +493 -0
  250. data/doc/docs/h-save_md.html +453 -0
  251. data/doc/docs/h-service_md.html +654 -0
  252. data/doc/docs/h-session_md.html +370 -762
  253. data/doc/docs/h-sha_md.html +461 -0
  254. data/doc/docs/h-sparse_md.html +493 -0
  255. data/doc/docs/h-subtask_md.html +359 -0
  256. data/doc/docs/h-tags_md.html +417 -0
  257. data/doc/docs/h-task_md.html +447 -0
  258. data/doc/docs/h-title_md.html +400 -0
  259. data/doc/docs/h-todo_md.html +617 -0
  260. data/doc/docs/h-window_md.html +356 -785
  261. data/doc/docs/h-wtree_md.html +539 -0
  262. data/doc/docs/h_md.html +605 -0
  263. data/doc/docs/t_md.html +659 -0
  264. data/doc/docs/why-t_md.html +698 -0
  265. data/doc/fonts/Lato-Light.ttf +0 -0
  266. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  267. data/doc/fonts/Lato-Regular.ttf +0 -0
  268. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  269. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  270. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  271. data/doc/h-tmux-plugins_tmux.html +309 -669
  272. data/doc/herdr-plugin/herdr-plugin_toml.html +375 -0
  273. data/doc/hiiro_gemspec.html +621 -0
  274. data/doc/images/add.png +0 -0
  275. data/doc/images/arrow_up.png +0 -0
  276. data/doc/images/brick.png +0 -0
  277. data/doc/images/brick_link.png +0 -0
  278. data/doc/images/bug.png +0 -0
  279. data/doc/images/bullet_black.png +0 -0
  280. data/doc/images/bullet_toggle_minus.png +0 -0
  281. data/doc/images/bullet_toggle_plus.png +0 -0
  282. data/doc/images/date.png +0 -0
  283. data/doc/images/delete.png +0 -0
  284. data/doc/images/find.png +0 -0
  285. data/doc/images/loadingAnimation.gif +0 -0
  286. data/doc/images/macFFBgHack.png +0 -0
  287. data/doc/images/package.png +0 -0
  288. data/doc/images/page_green.png +0 -0
  289. data/doc/images/page_white_text.png +0 -0
  290. data/doc/images/page_white_width.png +0 -0
  291. data/doc/images/plugin.png +0 -0
  292. data/doc/images/ruby.png +0 -0
  293. data/doc/images/tag_blue.png +0 -0
  294. data/doc/images/tag_green.png +0 -0
  295. data/doc/images/transparent.png +0 -0
  296. data/doc/images/wrench.png +0 -0
  297. data/doc/images/wrench_orange.png +0 -0
  298. data/doc/images/zoom.png +0 -0
  299. data/doc/index.html +313 -667
  300. data/doc/js/darkfish.js +120 -0
  301. data/doc/js/search.js +110 -0
  302. data/doc/merge.html +353 -0
  303. data/doc/notes.html +328 -688
  304. data/doc/obsidian_slides_md.html +353 -687
  305. data/doc/record-demo_sh.html +313 -673
  306. data/doc/script/compare.html +310 -670
  307. data/doc/script/install.html +309 -669
  308. data/doc/script/sync.html +312 -672
  309. data/doc/script/test.html +309 -669
  310. data/doc/script/update.html +323 -667
  311. data/doc/side-effect-separation-plan_md.html +439 -0
  312. data/doc/slides_md.html +374 -727
  313. data/doc/table_of_contents.html +10197 -0
  314. data/docs/h-herdr.md +21 -8
  315. data/lib/hiiro/version.rb +1 -1
  316. data/script/publish +1 -1
  317. metadata +230 -3
  318. data/.DS_Store +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d2d48728418d4a6d30a48f702f0aa89ba3d6d7d9552904a4da2bcbdc78ccdde
4
- data.tar.gz: 3e14943d3dbbafab65ebc8fc11e763f88af12476d217b67a51f32a02ab1d536b
3
+ metadata.gz: 1877449a8336392c36f54f38f1ea86792b736f6d8af25bea8bb98f1f62081328
4
+ data.tar.gz: 495465e2395a43cc24464922aa60e24283e97bf715f7492f7be182e8936629c6
5
5
  SHA512:
6
- metadata.gz: 970a8d0e22b46b7a1c20ca873d6ecb1d331c81a887d3bddffc15f743b5d0a9666ff823cdae7dfc03692116f1da77adfd47cc76d82087f0e66fe57a220270e5c4
7
- data.tar.gz: 62d1f129d289f5cc3363aaaf69a4766366f26774396f23685aada9dc0115745ad09d9fdd1687004311f58b22e40b827d79cb64b13db7f191afdfd458cda6153e
6
+ metadata.gz: 0d1c6e96f27300ab32b675f10b2d00c207792a0534859bbbfaa02f72d855b3af33572422842474cdfddf3a18cd5146eb93060349b03671232b4b4485c262079d
7
+ data.tar.gz: 0db00cb34ddc01b743a1a3f039e80308619134ce722e9f316cc483f36c1db1579d749218be66ab30e31b664996f6eeb9e89a6ffacff6c1763ce8d47dc8193e7e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ### Added
6
+ - `h herdr` and `hh` pane controls for Vim-style focus, two-pane reflow, resize, swap, and zoom
7
+ - `prefix+shift+h/j/k/l` reflows a two-pane tab toward an edge; `prefix+alt+h/j/k/l` resizes the current pane
8
+
9
+ ## [0.1.384] - 2026-09-23
10
+
11
+ ### Added
12
+ - `h plist` command for working with property lists
13
+
14
+ ### Fixed
15
+ - publish script now requires hiiro unconditionally at startup instead of only when falling back to local version
16
+
3
17
  ## [0.1.381] - 2026-09-20
4
18
 
5
19
  ### Added
@@ -14,8 +28,6 @@
14
28
  - `h save` subcommands: `ls`/`list`, `dir`, `show`/`cat`, `copy`, `open`, `edit`, `rm`/`remove` for managing saved files
15
29
  - `t path`, `t cd`, `t start`, and `t switch` accept optional `APP` argument to select a configured relative directory beneath the task root
16
30
  - APP resolves by exact name or unique case-sensitive prefix; fails when ambiguous or missing
17
-
18
- ### Added
19
31
  - `t switch` / `t workspace` and `t cd` accept an optional APP positional (or `--app`) resolving through the apps registry by exact name or unique prefix; the workspace or pane cd targets the app directory under the task worktree, e.g. `t switch aldi cpm` opens `~/work/aldi/main/retailer-tools/content-page-migrator`
20
32
 
21
33
  ## [0.1.376] - 2026-09-14
@@ -143,14 +155,4 @@
143
155
 
144
156
  ### Removed
145
157
  - Remove tmux paste-buffer, jumplist, task-color, focus-hook, and arbitrary-layout features that Herdr 0.8.2 does not support.
146
- - Remove the old `Hiiro::Tmux` adapter and tmux-specific command tests.
147
-
148
- ### Fixed
149
- - Strip the desktop notifier executable path and run notifications and sounds as detached processes instead of creating persistent Herdr tabs.
150
- - Preserve `add_cmd` declaration locations and argument metadata in generated help; allow command groups to pass arguments and help through to child commands.
151
- - Make undeclared `add_cmd opts:` entries boolean flags without consuming positional arguments; preserve explicit options and reserve conflicting short aliases.
152
- - Show selected command options for `add_cmd -h`/`--help` without executing the command block.
153
- - Focus the exact Herdr pane through the socket API and read the CLI's plain-text pane output without JSON parsing.
154
- - Restore the missing `Hiiro::Bins` helper so `require "hiiro"` boots and commands like `h jumplist record` dispatch correctly.
155
- - Make Hiiro's Ruby requirement explicit as Ruby 3.2+ and have rbenv-wide gem installs skip incompatible Ruby versions.
156
- - Update the publish script to preserve the Ruby support constant, run only on supported Ruby, and install releases only into compatible rbenv versions.
158
+ - Remove the old `Hiiro::Tmux` adapter and tmux-specific command tests.
data/README.md CHANGED
@@ -152,7 +152,7 @@ See the [task command reference](docs/t.md) for all commands and the [workflow i
152
152
  | `h setup` | Install plugins and subcommands to system paths |
153
153
  | `h edit` | Open the h script in your editor |
154
154
  | `h alert` | macOS desktop notifications via terminal-notifier |
155
- | `h herdr` | Herdr plugin for `t`: fuzzy task switcher, todo capture, todo copy, task shell and nvim popups (`h herdr install`) |
155
+ | `h herdr` | Herdr task popups plus Vim-style pane focus, movement, resize, swap, and zoom controls (`h herdr install`) |
156
156
  | `h task` | Same program as `t`: task records, todos, worktrees (`t NAME tree new`), and Herdr workspaces |
157
157
 
158
158
  ### External Subcommands
data/bin/h-herdr CHANGED
@@ -10,6 +10,16 @@ require 'fileutils'
10
10
 
11
11
  PLUGIN_ID = 'hiiro'
12
12
  ACTIONS = %w[switch todo-add todos shell nvim].freeze
13
+ PANE_DIRECTIONS = {
14
+ 'h' => 'left',
15
+ 'j' => 'down',
16
+ 'k' => 'up',
17
+ 'l' => 'right',
18
+ 'left' => 'left',
19
+ 'down' => 'down',
20
+ 'up' => 'up',
21
+ 'right' => 'right',
22
+ }.freeze
13
23
  INSTALL_DIR = Hiiro::Config.config_dir('herdr-plugin')
14
24
  SOURCE_DIR = File.expand_path('../herdr-plugin', File.dirname(File.realpath(__FILE__)))
15
25
  KEYS_SNIPPET = <<~TOML
@@ -43,6 +53,56 @@ KEYS_SNIPPET = <<~TOML
43
53
  type = "plugin_action"
44
54
  command = "hiiro.nvim"
45
55
  description = "task notes in nvim"
56
+
57
+ # Vim-style pane layout controls. Herdr already uses prefix+h/j/k/l for focus.
58
+ # Uppercase rotates a two-pane tab so the current pane lands on that edge.
59
+ [[keys.command]]
60
+ key = "prefix+shift+h"
61
+ type = "shell"
62
+ command = "h herdr move left"
63
+ description = "move current pane to the left"
64
+
65
+ [[keys.command]]
66
+ key = "prefix+shift+j"
67
+ type = "shell"
68
+ command = "h herdr move down"
69
+ description = "move current pane to the bottom"
70
+
71
+ [[keys.command]]
72
+ key = "prefix+shift+k"
73
+ type = "shell"
74
+ command = "h herdr move up"
75
+ description = "move current pane to the top"
76
+
77
+ [[keys.command]]
78
+ key = "prefix+shift+l"
79
+ type = "shell"
80
+ command = "h herdr move right"
81
+ description = "move current pane to the right"
82
+
83
+ [[keys.command]]
84
+ key = "prefix+alt+h"
85
+ type = "shell"
86
+ command = "h herdr resize left"
87
+ description = "resize current pane left"
88
+
89
+ [[keys.command]]
90
+ key = "prefix+alt+j"
91
+ type = "shell"
92
+ command = "h herdr resize down"
93
+ description = "resize current pane down"
94
+
95
+ [[keys.command]]
96
+ key = "prefix+alt+k"
97
+ type = "shell"
98
+ command = "h herdr resize up"
99
+ description = "resize current pane up"
100
+
101
+ [[keys.command]]
102
+ key = "prefix+alt+l"
103
+ type = "shell"
104
+ command = "h herdr resize right"
105
+ description = "resize current pane right"
46
106
  TOML
47
107
 
48
108
  def herdr_bin
@@ -55,6 +115,45 @@ def source_dir
55
115
  gem_root && File.join(gem_root, 'herdr-plugin')
56
116
  end
57
117
 
118
+ def pane_direction!(value)
119
+ PANE_DIRECTIONS.fetch(value.to_s) do
120
+ raise Hiiro::Error, 'Direction must be h, j, k, l, left, down, up, or right'
121
+ end
122
+ end
123
+
124
+ def pane_ratio!(value)
125
+ return nil if value.nil?
126
+ ratio = Float(value, exception: false)
127
+ raise Hiiro::Error, 'Ratio must be greater than 0 and less than 1' unless ratio && ratio.positive? && ratio < 1
128
+ ratio
129
+ end
130
+
131
+ def move_current_pane(direction, ratio = nil)
132
+ current = herdr_client.current_pane
133
+ raise Hiiro::Error, 'No current Herdr pane' unless current
134
+ panes = herdr_client.panes(workspace: current.workspace_id).select { |pane| pane.tab_id == current.tab_id }
135
+ raise Hiiro::Error, "Move requires exactly two panes in the current tab; found #{panes.length}" unless panes.length == 2
136
+ other = panes.find { |pane| pane.id != current.id }
137
+
138
+ if %w[left up].include?(direction)
139
+ source, target = other, current
140
+ split = direction == 'left' ? 'right' : 'down'
141
+ move_ratio = ratio && 1.0 - ratio
142
+ focus = '--no-focus'
143
+ else
144
+ source, target = current, other
145
+ split = direction
146
+ move_ratio = ratio
147
+ focus = '--focus'
148
+ end
149
+
150
+ detached = system(herdr_bin, 'pane', 'move', source.id, '--new-tab', '--no-focus')
151
+ raise Hiiro::Error, 'Herdr could not detach the pane for reflow' unless detached
152
+ args = ['pane', 'move', source.id, '--tab', target.tab_id, '--target-pane', target.id, '--split', split]
153
+ args += ['--ratio', move_ratio.to_s] if move_ratio
154
+ raise Hiiro::Error, 'Herdr could not reflow the pane' unless system(herdr_bin, *args, focus)
155
+ end
156
+
58
157
  Hiiro.run(*ARGV, external_commands: false) do
59
158
  add_cmd(:install) do
60
159
  no_args = opts.args.empty?
@@ -81,6 +180,33 @@ Hiiro.run(*ARGV, external_commands: false) do
81
180
 
82
181
  add_cmd(:actions) { ACTIONS.each { |name| puts "hiiro.#{name}" } }
83
182
 
183
+ add_cmd(:focus, args: %i[direction]) do
184
+ direction = pane_direction!(opts.args.first)
185
+ raise Hiiro::Error, 'Herdr could not focus the pane' unless system(herdr_bin, 'pane', 'focus', '--current', '--direction', direction)
186
+ end
187
+
188
+ add_cmd(:resize, args: %i[direction amount?]) do
189
+ direction = pane_direction!(opts.args.shift)
190
+ amount = opts.args.shift
191
+ args = ['pane', 'resize', '--current', '--direction', direction]
192
+ args += ['--amount', amount] if amount
193
+ raise Hiiro::Error, 'Herdr could not resize the pane' unless system(herdr_bin, *args)
194
+ end
195
+
196
+ add_cmd(:swap, args: %i[direction]) do
197
+ direction = pane_direction!(opts.args.first)
198
+ raise Hiiro::Error, 'Herdr could not swap the pane' unless system(herdr_bin, 'pane', 'swap', '--current', '--direction', direction)
199
+ end
200
+
201
+ add_cmd(:move, :layout, :arrange, args: %i[direction ratio?]) do
202
+ move_current_pane(pane_direction!(opts.args.shift), pane_ratio!(opts.args.shift))
203
+ end
204
+
205
+ add_cmd(:zoom) do
206
+ raise Hiiro::Error, 'h herdr zoom takes no arguments' unless opts.args.empty?
207
+ raise Hiiro::Error, 'Herdr could not zoom the pane' unless system(herdr_bin, 'pane', 'zoom', '--current', '--toggle')
208
+ end
209
+
84
210
  # Headless: bound to a key, opens the popup with the chosen action.
85
211
  add_cmd(:action, args: %i[name]) do
86
212
  name = opts.args.first
data/bin/h-js CHANGED
@@ -5,6 +5,27 @@ require 'hiiro'
5
5
  # sed -n '/scripts.*[{]/,/[}]/p' package.json
6
6
 
7
7
  Hiiro.run do
8
+ add_cmd :add, args: ['NAME', 'COMMAND...'] do
9
+ abort 'Usage: h-js add NAME COMMAND... (use -- before command flags)' unless opts.args.length >= 2
10
+ name, *command_args = opts.args
11
+ abort 'Script name cannot be blank' if name.strip.empty?
12
+ command = command_args.length == 1 ? command_args.first : Shellwords.join(command_args)
13
+ abort 'Script command cannot be blank' if command.strip.empty?
14
+ abort 'No package.json in the current directory' unless File.file?('package.json')
15
+
16
+ package = JSON.parse(File.read('package.json'))
17
+ abort 'package.json must contain an object' unless package.is_a?(Hash)
18
+ package['scripts'] = {} unless package.key?('scripts')
19
+ abort 'package.json scripts must be an object' unless package['scripts'].is_a?(Hash)
20
+ abort "Script #{name.inspect} already exists" if package['scripts'].key?(name)
21
+
22
+ package['scripts'][name] = command
23
+ File.write('package.json', JSON.pretty_generate(package) + "\n")
24
+ puts "Added script #{name.inspect} to package.json"
25
+ rescue JSON::ParserError => error
26
+ abort "Invalid package.json: #{error.message}"
27
+ end
28
+
8
29
  add_cmd :pkg do
9
30
  file, *_other = Dir.glob('package.json')
10
31
 
data/bin/h-plist ADDED
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'hiiro'
4
+ require 'open3'
5
+ require 'json'
6
+
7
+ PLIST_TYPES = %w[bool integer float date string data xml json dictionary array].freeze
8
+ PLIST_FORMATS = %w[xml1 binary1 json swift objc].freeze
9
+
10
+ def require_plist(path)
11
+ unless path && !path.empty?
12
+ STDERR.puts 'A plist path is required.'
13
+ exit 1
14
+ end
15
+ expanded = File.expand_path(path)
16
+ unless File.file?(expanded)
17
+ STDERR.puts "Plist not found: #{expanded}"
18
+ exit 1
19
+ end
20
+ expanded
21
+ end
22
+
23
+ def run_plutil(*arguments, allow_failure: false)
24
+ stdout, stderr, status = Open3.capture3('plutil', *arguments)
25
+ unless status.success? || allow_failure
26
+ STDERR.print(stderr.empty? ? stdout : stderr)
27
+ exit status.exitstatus
28
+ end
29
+ [stdout, stderr, status]
30
+ end
31
+
32
+ def print_plutil(*arguments)
33
+ stdout, stderr, = run_plutil(*arguments)
34
+ STDOUT.print(stdout)
35
+ STDERR.print(stderr)
36
+ end
37
+
38
+ def validate_type(type)
39
+ return type if PLIST_TYPES.include?(type)
40
+ STDERR.puts "Unknown type #{type.inspect}. Use: #{PLIST_TYPES.join(', ')}"
41
+ exit 1
42
+ end
43
+
44
+ def mutation_arguments(verb, key, type, values)
45
+ args = [verb, key, "-#{validate_type(type)}"]
46
+ unless %w[dictionary array].include?(type)
47
+ if values.empty?
48
+ STDERR.puts "A value is required for type #{type}."
49
+ exit 1
50
+ end
51
+ args << values.join(' ')
52
+ end
53
+ args
54
+ end
55
+
56
+ Hiiro.run(*ARGV) do
57
+ add_subcmd(:show, :print) do |path=nil|
58
+ print_plutil('-p', '--', require_plist(path))
59
+ end
60
+
61
+ add_subcmd(:get) do |path=nil, key=nil, format='raw'|
62
+ unless key
63
+ STDERR.puts 'Usage: h plist get <file> <keypath> [raw|json|xml1|binary1]'
64
+ exit 1
65
+ end
66
+ unless %w[raw json xml1 binary1].include?(format)
67
+ STDERR.puts 'Format must be raw, json, xml1, or binary1.'
68
+ exit 1
69
+ end
70
+ print_plutil('-extract', key, format, '-o', '-', '--', require_plist(path))
71
+ end
72
+
73
+ add_subcmd(:keys) do |path=nil, key=nil|
74
+ plist = require_plist(path)
75
+ stdout, = if key
76
+ run_plutil('-extract', key, 'json', '-expect', 'dictionary', '-o', '-', '--', plist)
77
+ else
78
+ run_plutil('-convert', 'json', '-o', '-', '--', plist)
79
+ end
80
+ value = JSON.parse(stdout)
81
+ unless value.is_a?(Hash)
82
+ STDERR.puts(key ? "Not a dictionary: #{key}" : 'The plist root is not a dictionary.')
83
+ exit 1
84
+ end
85
+ puts value.keys.sort
86
+ end
87
+
88
+ add_subcmd(:type) do |path=nil, key=nil|
89
+ unless key
90
+ STDERR.puts 'Usage: h plist type <file> <keypath>'
91
+ exit 1
92
+ end
93
+ print_plutil('-type', key, '--', require_plist(path))
94
+ end
95
+
96
+ add_subcmd(:lint) do |*paths|
97
+ if paths.empty?
98
+ STDERR.puts 'Usage: h plist lint <file...>'
99
+ exit 1
100
+ end
101
+ expanded = paths.map { |path| require_plist(path) }
102
+ print_plutil('-lint', '--', *expanded)
103
+ end
104
+
105
+ add_subcmd(:set) do |path=nil, key=nil, *arguments|
106
+ unless key
107
+ STDERR.puts 'Usage: h plist set <file> <keypath> [type] <value>'
108
+ exit 1
109
+ end
110
+ type = PLIST_TYPES.include?(arguments.first) ? arguments.shift : 'string'
111
+ values = arguments
112
+ plist = require_plist(path)
113
+ _, _, status = run_plutil('-type', key, '--', plist, allow_failure: true)
114
+ verb = status.success? ? '-replace' : '-insert'
115
+ print_plutil(*mutation_arguments(verb, key, type, values), '--', plist)
116
+ end
117
+
118
+ add_subcmd(:add, :insert) do |path=nil, key=nil, *arguments|
119
+ unless key
120
+ STDERR.puts 'Usage: h plist add <file> <keypath> [type] <value>'
121
+ exit 1
122
+ end
123
+ type = PLIST_TYPES.include?(arguments.first) ? arguments.shift : 'string'
124
+ values = arguments
125
+ print_plutil(*mutation_arguments('-insert', key, type, values), '--', require_plist(path))
126
+ end
127
+
128
+ add_subcmd(:rm, :remove, :delete) do |path=nil, key=nil|
129
+ unless key
130
+ STDERR.puts 'Usage: h plist rm <file> <keypath>'
131
+ exit 1
132
+ end
133
+ print_plutil('-remove', key, '--', require_plist(path))
134
+ end
135
+
136
+ add_subcmd(:convert) do |path=nil, format=nil, output=nil|
137
+ plist = require_plist(path)
138
+ unless PLIST_FORMATS.include?(format)
139
+ STDERR.puts "Usage: h plist convert <file> <#{PLIST_FORMATS.join('|')}> [output]"
140
+ exit 1
141
+ end
142
+ args = ['-convert', format]
143
+ args.concat(['-o', File.expand_path(output)]) if output
144
+ print_plutil(*args, '--', plist)
145
+ end
146
+
147
+ add_subcmd(:json) do |path=nil|
148
+ print_plutil('-convert', 'json', '-r', '-o', '-', '--', require_plist(path))
149
+ end
150
+
151
+ add_subcmd(:xml) do |path=nil|
152
+ print_plutil('-convert', 'xml1', '-o', '-', '--', require_plist(path))
153
+ end
154
+
155
+ add_subcmd(:create) do |path=nil, format='xml1'|
156
+ unless path
157
+ STDERR.puts 'Usage: h plist create <file> [xml1|binary1|json]'
158
+ exit 1
159
+ end
160
+ unless %w[xml1 binary1 json].include?(format)
161
+ STDERR.puts 'Create format must be xml1, binary1, or json.'
162
+ exit 1
163
+ end
164
+ expanded = File.expand_path(path)
165
+ if File.exist?(expanded)
166
+ STDERR.puts "Refusing to overwrite existing file: #{expanded}"
167
+ exit 1
168
+ end
169
+ print_plutil('-create', format, expanded)
170
+ end
171
+
172
+ add_subcmd(:edit) do |path=nil|
173
+ exec('safe_nvim', require_plist(path))
174
+ end
175
+
176
+ add_subcmd(:help) do
177
+ puts <<~HELP
178
+ h-plist — inspect and modify Apple property lists with plutil
179
+
180
+ READ:
181
+ h plist <file> Pretty-print a plist
182
+ h plist show <file> Pretty-print a plist
183
+ h plist get <file> <keypath> [format] Extract a value
184
+ h plist keys <file> [dict-keypath] List dictionary keys
185
+ h plist type <file> <keypath> Print a value's type
186
+ h plist lint <file...> Validate plist syntax
187
+ h plist json <file> Emit readable JSON
188
+ h plist xml <file> Emit XML
189
+
190
+ WRITE:
191
+ h plist set <file> <keypath> [type] <value> Insert or replace
192
+ h plist add <file> <keypath> [type] <value> Insert only
193
+ h plist rm <file> <keypath> Remove a value
194
+ h plist convert <file> <format> [output] Convert in place or copy
195
+ h plist create <file> [format] Create without overwrite
196
+ h plist edit <file> Open with safe_nvim
197
+
198
+ TYPES:
199
+ bool, integer, float, date, string (default), data, xml, json,
200
+ dictionary, array
201
+
202
+ Key paths use plutil syntax. Numeric components address array indexes.
203
+ Quote values containing spaces. Mutating commands edit the file in place.
204
+ HELP
205
+ end
206
+
207
+ add_default do |path=nil|
208
+ if path
209
+ print_plutil('-p', '--', require_plist(path))
210
+ else
211
+ run_subcmd(:help)
212
+ end
213
+ end
214
+ end
data/bin/hh CHANGED
@@ -44,6 +44,10 @@ Hiiro.run do
44
44
  end
45
45
  end
46
46
 
47
+ %i[focus resize swap move layout arrange zoom].each do |name|
48
+ add_cmd(name) { |*args| system('h', 'herdr', name.to_s, *args) }
49
+ end
50
+
47
51
  add_default do |*args|
48
52
  system 'herdr', *args
49
53
  end