hiiro 0.1.383 → 0.1.384

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 (314) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -13
  3. data/bin/h-js +21 -0
  4. data/bin/h-plist +214 -0
  5. data/doc/AGENTS_md.html +898 -0
  6. data/doc/AppTest/MockApp.html +192 -0
  7. data/doc/AppTest/MockEnvironment.html +192 -0
  8. data/doc/AppTest/MockGit.html +157 -0
  9. data/doc/AppTest/MockItem.html +178 -0
  10. data/doc/AppTest/MockMatcher.html +200 -0
  11. data/doc/AppTest/MockResult.html +235 -0
  12. data/doc/AppTest.html +401 -0
  13. data/doc/Asarfile/Appfile.html +4219 -0
  14. data/doc/Asarfile.html +1445 -0
  15. data/doc/BackgroundTest.html +388 -0
  16. data/doc/BinsTest.html +245 -0
  17. data/doc/BranchManager.html +373 -804
  18. data/doc/BranchTest/MockGit.html +272 -0
  19. data/doc/BranchTest.html +621 -0
  20. data/doc/CHANGELOG_md.html +771 -0
  21. data/doc/CLAUDE_md.html +310 -1092
  22. data/doc/ClaudeTest.html +323 -0
  23. data/doc/CommitTest.html +271 -0
  24. data/doc/ConfigTest.html +407 -0
  25. data/doc/ContextBundleManager.html +811 -0
  26. data/doc/CurrentTaskTest.html +325 -0
  27. data/doc/Directory.html +63 -770
  28. data/doc/EnvFile.html +397 -0
  29. data/doc/EnvironmentTest.html +610 -0
  30. data/doc/FilesystemEffectsTest.html +478 -0
  31. data/doc/FuzzyfindTest.html +382 -0
  32. data/doc/Gemfile.html +309 -669
  33. data/doc/Gemfile_lock.html +336 -691
  34. data/doc/GitBranchTest.html +428 -0
  35. data/doc/GitBranchesTest.html +428 -0
  36. data/doc/GitEffectsTest.html +381 -0
  37. data/doc/GitPrTest.html +581 -0
  38. data/doc/GitRemoteTest.html +519 -0
  39. data/doc/GitWorktreeTest.html +576 -0
  40. data/doc/GitWorktreesTest.html +712 -0
  41. data/doc/Globber.html +593 -0
  42. data/doc/HerdrEffectsTest.html +464 -0
  43. data/doc/HerdrPluginTest.html +344 -0
  44. data/doc/HerdrWorkspaceTest.html +230 -0
  45. data/doc/Hiiro/AnyStruct.html +78 -835
  46. data/doc/Hiiro/AppFiles.html +88 -882
  47. data/doc/Hiiro/AppRecord.html +233 -0
  48. data/doc/Hiiro/Args.html +82 -855
  49. data/doc/Hiiro/Assignment.html +232 -0
  50. data/doc/Hiiro/Background.html +287 -0
  51. data/doc/Hiiro/Bins.html +70 -818
  52. data/doc/Hiiro/Branch.html +416 -0
  53. data/doc/Hiiro/Capture.html +314 -0
  54. data/doc/Hiiro/CheckRun.html +261 -0
  55. data/doc/Hiiro/Config.html +178 -830
  56. data/doc/Hiiro/CurrentTask.html +254 -0
  57. data/doc/Hiiro/DB/JSON.html +200 -0
  58. data/doc/Hiiro/DB.html +440 -0
  59. data/doc/Hiiro/Effects/Executor/Filesystem/NullFilesystem.html +642 -0
  60. data/doc/Hiiro/Effects/Executor/Filesystem.html +750 -0
  61. data/doc/Hiiro/Effects/Executor/NullExecutor.html +436 -0
  62. data/doc/Hiiro/Effects/Executor.html +509 -0
  63. data/doc/Hiiro/Effects.html +121 -0
  64. data/doc/Hiiro/Error.html +127 -0
  65. data/doc/Hiiro/FallbackTarget/App.html +308 -0
  66. data/doc/Hiiro/FallbackTarget/Environment.html +777 -0
  67. data/doc/Hiiro/FallbackTarget.html +1748 -0
  68. data/doc/Hiiro/Fuzzyfind.html +70 -818
  69. data/doc/Hiiro/Git/Branch.html +106 -975
  70. data/doc/Hiiro/Git/Branches.html +128 -1084
  71. data/doc/Hiiro/Git/Pr.html +2575 -1115
  72. data/doc/Hiiro/Git/Remote.html +106 -971
  73. data/doc/Hiiro/Git/Worktree.html +90 -899
  74. data/doc/Hiiro/Git/Worktrees.html +129 -1085
  75. data/doc/Hiiro/Git.html +306 -1413
  76. data/doc/Hiiro/Glob.html +58 -757
  77. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces/Tab/Tabs/Pane/Panes.html +5289 -0
  78. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces/Tab/Tabs/Pane.html +5470 -0
  79. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces/Tab/Tabs.html +2292 -0
  80. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces/Tab.html +2619 -0
  81. data/doc/Hiiro/Herdr/Collection/Workspace/Workspaces.html +1339 -0
  82. data/doc/Hiiro/Herdr/Collection/Workspace.html +2337 -0
  83. data/doc/Hiiro/Herdr/Collection.html +2190 -0
  84. data/doc/Hiiro/Herdr.html +125 -0
  85. data/doc/Hiiro/InputFile.html +550 -0
  86. data/doc/Hiiro/Invocation.html +356 -0
  87. data/doc/Hiiro/InvocationResolution.html +218 -0
  88. data/doc/Hiiro/Link.html +507 -0
  89. data/doc/Hiiro/Matcher/Item.html +60 -777
  90. data/doc/Hiiro/Matcher/PathResult.html +106 -979
  91. data/doc/Hiiro/Matcher/Result.html +110 -1001
  92. data/doc/Hiiro/Matcher.html +147 -1138
  93. data/doc/Hiiro/Notification.html +159 -953
  94. data/doc/Hiiro/Options/Args/Definition.html +557 -0
  95. data/doc/Hiiro/Options/Args.html +1301 -1020
  96. data/doc/Hiiro/Options.html +155 -963
  97. data/doc/Hiiro/PaneHome.html +312 -0
  98. data/doc/Hiiro/Paths/Symlink.html +102 -946
  99. data/doc/Hiiro/Paths.html +75 -838
  100. data/doc/Hiiro/PinRecord.html +309 -0
  101. data/doc/Hiiro/PinnedPRManager.html +1388 -0
  102. data/doc/Hiiro/PinnedPr.html +2261 -0
  103. data/doc/Hiiro/Project.html +233 -0
  104. data/doc/Hiiro/Projects.html +437 -0
  105. data/doc/Hiiro/PsProcess.html +770 -0
  106. data/doc/Hiiro/Queue/Prompt.html +177 -927
  107. data/doc/Hiiro/Queue.html +963 -1622
  108. data/doc/Hiiro/Rbenv.html +197 -988
  109. data/doc/Hiiro/RegistryEntry/Hiiro.html +176 -0
  110. data/doc/Hiiro/RegistryEntry.html +842 -0
  111. data/doc/Hiiro/Reminder.html +238 -0
  112. data/doc/Hiiro/Result.html +462 -0
  113. data/doc/Hiiro/RunnerTool.html +106 -956
  114. data/doc/Hiiro/Runners/Bin.html +98 -939
  115. data/doc/Hiiro/Runners/Subcommand.html +100 -956
  116. data/doc/Hiiro/Runners.html +150 -1043
  117. data/doc/Hiiro/ServiceManager.html +251 -1283
  118. data/doc/Hiiro/Shell.html +226 -772
  119. data/doc/Hiiro/ShellCommand.html +277 -0
  120. data/doc/Hiiro/SparseGroups.html +64 -784
  121. data/doc/Hiiro/Tag.html +543 -0
  122. data/doc/Hiiro/Tags.html +117 -906
  123. data/doc/Hiiro/Task.html +203 -982
  124. data/doc/Hiiro/TaskCli/Commands/Error.html +137 -0
  125. data/doc/Hiiro/TaskCli/Commands.html +3128 -0
  126. data/doc/Hiiro/TaskCli.html +220 -0
  127. data/doc/Hiiro/TaskManager/Config.html +110 -876
  128. data/doc/Hiiro/TaskManager.html +530 -1372
  129. data/doc/Hiiro/TaskRecord.html +511 -0
  130. data/doc/Hiiro/TaskResource.html +176 -0
  131. data/doc/Hiiro/TaskSessions/Error.html +131 -0
  132. data/doc/Hiiro/TaskSessions.html +247 -0
  133. data/doc/Hiiro/Tasks.html +289 -776
  134. data/doc/Hiiro/TestHarness.html +1105 -0
  135. data/doc/Hiiro/TodoItem.html +184 -1165
  136. data/doc/Hiiro/TodoManager.html +163 -1185
  137. data/doc/Hiiro/Tree.html +123 -898
  138. data/doc/Hiiro/Tui/ListScreen.html +672 -0
  139. data/doc/Hiiro/Tui/Terminal.html +488 -0
  140. data/doc/Hiiro/Tui.html +121 -0
  141. data/doc/Hiiro.html +631 -1851
  142. data/doc/HiiroAddCmdTest.html +366 -0
  143. data/doc/HiiroAddDefaultTest.html +384 -0
  144. data/doc/HiiroRunnersBinTest.html +363 -0
  145. data/doc/HiiroRunnersSubcommandTest.html +430 -0
  146. data/doc/HiiroSetup.html +190 -0
  147. data/doc/HiiroTest.html +533 -0
  148. data/doc/HomeDirs.html +528 -0
  149. data/doc/JsTest.html +187 -0
  150. data/doc/LICENSE.html +309 -669
  151. data/doc/LinkManager/Link.html +86 -885
  152. data/doc/LinkManager.html +224 -1007
  153. data/doc/LinkTest/MockLinkManager.html +331 -0
  154. data/doc/LinkTest.html +198 -0
  155. data/doc/MatcherItemTest.html +167 -0
  156. data/doc/MatcherResultTest.html +375 -0
  157. data/doc/MatcherTest.html +908 -0
  158. data/doc/MockHiiro.html +346 -0
  159. data/doc/NotificationTest/MockHiiroForNotify.html +180 -0
  160. data/doc/NotificationTest.html +592 -0
  161. data/doc/Notify.html +77 -828
  162. data/doc/NotifyPluginTest.html +421 -0
  163. data/doc/Object.html +7684 -1547
  164. data/doc/ObsidianNotes.html +599 -0
  165. data/doc/OptionsDefinitionTest.html +348 -0
  166. data/doc/OptionsTest.html +1047 -0
  167. data/doc/PaneTest/MockHerdr.html +1113 -0
  168. data/doc/PaneTest.html +415 -0
  169. data/doc/Pins/Pin.html +355 -1062
  170. data/doc/Pins.html +73 -815
  171. data/doc/PinsPinTest/MockHiiro.html +178 -0
  172. data/doc/PinsPinTest.html +514 -0
  173. data/doc/PluginTest.html +388 -0
  174. data/doc/PortfolioApi.html +311 -0
  175. data/doc/Project.html +84 -941
  176. data/doc/ProjectPluginTest.html +507 -0
  177. data/doc/ProjectTest.html +249 -0
  178. data/doc/QueueHerdrTest.html +181 -0
  179. data/doc/README_md.html +557 -741
  180. data/doc/Rakefile.html +309 -669
  181. data/doc/ReminderManager/Reminder.html +626 -0
  182. data/doc/ReminderManager.html +469 -0
  183. data/doc/RequirePathsTest.html +168 -0
  184. data/doc/RubyGems/Version/Git/Claude.html +251 -0
  185. data/doc/RubyGems/Version/Git/RubyGem.html +214 -0
  186. data/doc/RubyGems/Version/Git.html +1175 -0
  187. data/doc/RubyGems/Version.html +379 -0
  188. data/doc/RubyGems.html +251 -0
  189. data/doc/SaveTest.html +352 -0
  190. data/doc/ServiceManagerHerdrTest.html +187 -0
  191. data/doc/SessionTest/MockHerdr.html +699 -0
  192. data/doc/SessionTest.html +343 -0
  193. data/doc/ShaTest.html +343 -0
  194. data/doc/ShellTest.html +430 -0
  195. data/doc/String.html +58 -757
  196. data/doc/SystemCallCapture.html +267 -0
  197. data/doc/TODO_md.html +311 -672
  198. data/doc/TaskCommandTest.html +2416 -0
  199. data/doc/TaskManager.html +1554 -1599
  200. data/doc/TaskManagerConfigTest.html +451 -0
  201. data/doc/TaskManagerFilterTasksTest.html +421 -0
  202. data/doc/TaskManagerFromWorktreeTest.html +308 -0
  203. data/doc/TaskTest.html +545 -0
  204. data/doc/TaskToggleTui.html +224 -0
  205. data/doc/TestHarnessEffectsTest.html +562 -0
  206. data/doc/TestHelpers.html +231 -0
  207. data/doc/TodoItemTest.html +983 -0
  208. data/doc/TodoManagerTest.html +1172 -0
  209. data/doc/TodoTest/MockTodoManager.html +398 -0
  210. data/doc/TodoTest.html +243 -0
  211. data/doc/TreeTest.html +477 -0
  212. data/doc/WindowTest/MockHerdr.html +746 -0
  213. data/doc/WindowTest.html +343 -0
  214. data/doc/WtreeTest.html +566 -0
  215. data/doc/bin/h-_config.html +362 -0
  216. data/doc/bin/h-dsstore-read.html +358 -0
  217. data/doc/css/fonts.css +167 -0
  218. data/doc/css/rdoc.css +328 -1600
  219. data/doc/demo_sh.html +314 -674
  220. data/doc/docs/README_md.html +477 -736
  221. data/doc/docs/coupling-and-extraction-plan_txt.html +1589 -0
  222. data/doc/docs/docker-testing-guide_md.html +668 -865
  223. data/doc/docs/h-app_md.html +615 -0
  224. data/doc/docs/h-bg_md.html +420 -0
  225. data/doc/docs/h-bin_md.html +475 -0
  226. data/doc/docs/h-branch_md.html +1035 -0
  227. data/doc/docs/h-claude_md.html +1121 -0
  228. data/doc/docs/h-commit_md.html +417 -0
  229. data/doc/docs/h-config_md.html +522 -0
  230. data/doc/docs/h-cpr_md.html +399 -0
  231. data/doc/docs/h-db_md.html +555 -0
  232. data/doc/docs/h-file_md.html +422 -0
  233. data/doc/docs/h-herdr_md.html +470 -0
  234. data/doc/docs/h-img_md.html +430 -0
  235. data/doc/docs/h-link_md.html +619 -0
  236. data/doc/docs/h-misc_md.html +435 -0
  237. data/doc/docs/h-notify_md.html +510 -0
  238. data/doc/docs/h-pane_md.html +405 -784
  239. data/doc/docs/h-plugin_md.html +382 -705
  240. data/doc/docs/h-pm_md.html +549 -0
  241. data/doc/docs/h-pr-monitor_md.html +458 -0
  242. data/doc/docs/h-pr_md.html +795 -0
  243. data/doc/docs/h-project_md.html +522 -0
  244. data/doc/docs/h-queue_md.html +744 -0
  245. data/doc/docs/h-registry_md.html +542 -0
  246. data/doc/docs/h-run_md.html +493 -0
  247. data/doc/docs/h-save_md.html +453 -0
  248. data/doc/docs/h-service_md.html +654 -0
  249. data/doc/docs/h-session_md.html +370 -762
  250. data/doc/docs/h-sha_md.html +461 -0
  251. data/doc/docs/h-sparse_md.html +493 -0
  252. data/doc/docs/h-subtask_md.html +359 -0
  253. data/doc/docs/h-tags_md.html +417 -0
  254. data/doc/docs/h-task_md.html +447 -0
  255. data/doc/docs/h-title_md.html +400 -0
  256. data/doc/docs/h-todo_md.html +617 -0
  257. data/doc/docs/h-window_md.html +356 -785
  258. data/doc/docs/h-wtree_md.html +539 -0
  259. data/doc/docs/h_md.html +605 -0
  260. data/doc/docs/t_md.html +659 -0
  261. data/doc/docs/why-t_md.html +698 -0
  262. data/doc/fonts/Lato-Light.ttf +0 -0
  263. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  264. data/doc/fonts/Lato-Regular.ttf +0 -0
  265. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  266. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  267. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  268. data/doc/h-tmux-plugins_tmux.html +309 -669
  269. data/doc/herdr-plugin/herdr-plugin_toml.html +375 -0
  270. data/doc/hiiro_gemspec.html +621 -0
  271. data/doc/images/add.png +0 -0
  272. data/doc/images/arrow_up.png +0 -0
  273. data/doc/images/brick.png +0 -0
  274. data/doc/images/brick_link.png +0 -0
  275. data/doc/images/bug.png +0 -0
  276. data/doc/images/bullet_black.png +0 -0
  277. data/doc/images/bullet_toggle_minus.png +0 -0
  278. data/doc/images/bullet_toggle_plus.png +0 -0
  279. data/doc/images/date.png +0 -0
  280. data/doc/images/delete.png +0 -0
  281. data/doc/images/find.png +0 -0
  282. data/doc/images/loadingAnimation.gif +0 -0
  283. data/doc/images/macFFBgHack.png +0 -0
  284. data/doc/images/package.png +0 -0
  285. data/doc/images/page_green.png +0 -0
  286. data/doc/images/page_white_text.png +0 -0
  287. data/doc/images/page_white_width.png +0 -0
  288. data/doc/images/plugin.png +0 -0
  289. data/doc/images/ruby.png +0 -0
  290. data/doc/images/tag_blue.png +0 -0
  291. data/doc/images/tag_green.png +0 -0
  292. data/doc/images/transparent.png +0 -0
  293. data/doc/images/wrench.png +0 -0
  294. data/doc/images/wrench_orange.png +0 -0
  295. data/doc/images/zoom.png +0 -0
  296. data/doc/index.html +313 -667
  297. data/doc/js/darkfish.js +120 -0
  298. data/doc/js/search.js +110 -0
  299. data/doc/merge.html +353 -0
  300. data/doc/notes.html +328 -688
  301. data/doc/obsidian_slides_md.html +353 -687
  302. data/doc/record-demo_sh.html +313 -673
  303. data/doc/script/compare.html +310 -670
  304. data/doc/script/install.html +309 -669
  305. data/doc/script/sync.html +312 -672
  306. data/doc/script/test.html +309 -669
  307. data/doc/script/update.html +323 -667
  308. data/doc/side-effect-separation-plan_md.html +439 -0
  309. data/doc/slides_md.html +374 -727
  310. data/doc/table_of_contents.html +10197 -0
  311. data/lib/hiiro/version.rb +1 -1
  312. data/script/publish +1 -1
  313. metadata +230 -3
  314. 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: 308d4ea2a261c3dd45eedd5b40bb0bcf8ada1b6cd2310bd908abac4e4d73c408
4
+ data.tar.gz: e138d7b8aeed4b9fa598dbe8d0d7dfb45abec78658c338a048d42d8d540a3a27
5
5
  SHA512:
6
- metadata.gz: 970a8d0e22b46b7a1c20ca873d6ecb1d331c81a887d3bddffc15f743b5d0a9666ff823cdae7dfc03692116f1da77adfd47cc76d82087f0e66fe57a220270e5c4
7
- data.tar.gz: 62d1f129d289f5cc3363aaaf69a4766366f26774396f23685aada9dc0115745ad09d9fdd1687004311f58b22e40b827d79cb64b13db7f191afdfd458cda6153e
6
+ metadata.gz: 9d0e4d5ab790f5c262d7d4c03517c7caf40214ad9e9e2b76e7df84724bdf19f105eff32c935bcdea3f73cc64ec3bdf5e922258ffed5c44cdd9969b2649e313b7
7
+ data.tar.gz: 74a0868f58e19659707345d4241f08ae0c112f2e925b2c5fe43e6faf008b7eceafdcc914aba27ce5f638ccd7c7c4b55a383ac7f3126cd78cc1e7b26a2daf263c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.384] - 2026-09-23
4
+
5
+ ### Added
6
+ - `h plist` command for working with property lists
7
+
8
+ ### Fixed
9
+ - publish script now requires hiiro unconditionally at startup instead of only when falling back to local version
10
+
3
11
  ## [0.1.381] - 2026-09-20
4
12
 
5
13
  ### Added
@@ -14,8 +22,6 @@
14
22
  - `h save` subcommands: `ls`/`list`, `dir`, `show`/`cat`, `copy`, `open`, `edit`, `rm`/`remove` for managing saved files
15
23
  - `t path`, `t cd`, `t start`, and `t switch` accept optional `APP` argument to select a configured relative directory beneath the task root
16
24
  - APP resolves by exact name or unique case-sensitive prefix; fails when ambiguous or missing
17
-
18
- ### Added
19
25
  - `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
26
 
21
27
  ## [0.1.376] - 2026-09-14
@@ -143,14 +149,4 @@
143
149
 
144
150
  ### Removed
145
151
  - 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.
152
+ - Remove the old `Hiiro::Tmux` adapter and tmux-specific command tests.
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