hiiro 0.1.30 → 0.1.32

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: 5ed8afa7ccba684e9508ee61648511cd08bc3ae84d74b43966a1b53d421f6b05
4
- data.tar.gz: 3fc69daaad45903fda5a0123cecc0170698e35fdf0f130a78f8f8b05d17876fe
3
+ metadata.gz: 33682128f00683ee714b4bc368dbcc2243544cf3c495063231ec95497ded5bc8
4
+ data.tar.gz: 8bfb329a9ec2f09a96e886c8a3ea3146ccb7a5a728ca5efc7a83509df3d578a0
5
5
  SHA512:
6
- metadata.gz: a1dded26ad1d42ca16d2cf50cb2d2385eb1d4bcf02215e47b655a6eb36a2a1e37bb91f80ca378691c85ca1226513806ab6baa4c9dd26dc140e1208228c31de8c
7
- data.tar.gz: 1b1510771c5fae9c2bf7210a65d100163d1446e3a2fef43d7af524cb32440783ca2b7234d00563a66fc62510bbf3a92dbe2c6891e64e91747d5d5aec72910b6b
6
+ metadata.gz: 6d4ad31db7afe3ca4ee5f7160e5004a086b8a5962908bc346bd30ca9a0c8a4d6948f1c699339d5103a14ea37b101b1beb8a515f17a154ec77e3b7a422cf8211e
7
+ data.tar.gz: b6f2348435f48f1ba9a2ce9b38cd7a6db4d5aefca7c32d8291e96ff6faf589d061f4b2d9838fc9643ff05988dfd9489680dd684e36a478f11203ceb8b40a311d
data/README.md CHANGED
@@ -19,7 +19,7 @@ See [docs/](docs/) for detailed documentation on all subcommands.
19
19
  gem install hiiro
20
20
 
21
21
  # Install plugins and subcommands
22
- hiiro setup
22
+ h setup
23
23
  ```
24
24
 
25
25
  This installs:
@@ -28,26 +28,15 @@ This installs:
28
28
 
29
29
  Ensure `~/bin` is in your `$PATH`.
30
30
 
31
- ### Manual Installation
32
-
33
- ```sh
34
- # Copy the main script
35
- cp bin/h ~/bin/h
36
- chmod +x ~/bin/h
37
-
38
- # Copy subcommands (optional)
39
- cp bin/h-* ~/bin/
40
-
41
- # Copy plugins (optional)
42
- mkdir -p ~/.config/hiiro/plugins
43
- cp plugins/*.rb ~/.config/hiiro/plugins/
44
- ```
45
31
 
46
32
  ### Dependencies
47
33
 
48
34
  ```sh
49
35
  # For notify plugin (macOS)
50
36
  brew install terminal-notifier
37
+
38
+ # For fuzzy-finder
39
+ brew install sk
51
40
  ```
52
41
 
53
42
  ## Quick Start
@@ -56,14 +45,6 @@ brew install terminal-notifier
56
45
  # List available subcommands
57
46
  h
58
47
 
59
- # Edit the main h script
60
- h edit
61
-
62
- # Get paths
63
- h path # Print current directory
64
- h ppath # Print project root (git repo root + relative dir)
65
- h rpath # Print relative directory from git root
66
-
67
48
  # Simple test
68
49
  h ping
69
50
  # => pong
data/TODO.md ADDED
@@ -0,0 +1,21 @@
1
+
2
+ # To do...
3
+
4
+ - [ ] move task/subtask back into a plugin
5
+ - [ ] have it add :task and :subtask subcommands to the instance of Hiiro
6
+ - [ ] they should init another hiiro instance with a `scope` value set
7
+ - [ ] if scope is :task, it looks at the whole world
8
+ - [ ] if the scope is :subtask, it scopes behavior to the current task
9
+ - [ ] redesign the yml structure for managing tasks
10
+
11
+ ```yml
12
+ tasks:
13
+ - name: some_name
14
+ parent_task: parent_task_name (if included it's a subtask)
15
+ tree: tree_name
16
+ root: repo_root/tree_name
17
+ ```
18
+ - [ ] refactor task plugin to not be procedural AI slop
19
+ - [ ] convert to js and using node
20
+ - [ ] h-config {vim,zsh,tmux}
21
+ - [ ]
data/bin/g-pr CHANGED
@@ -7,7 +7,7 @@ require "yaml"
7
7
  require "json"
8
8
 
9
9
  Hiiro.load_env
10
- hiiro = Hiiro.init(*ARGV, plugins: [Task, Tmux, Pins])
10
+ hiiro = Hiiro.init(*ARGV, plugins: [OldTask, Tmux, Pins])
11
11
 
12
12
  class PRManager
13
13
  attr_reader :hiiro
data/bin/h-branch CHANGED
@@ -5,7 +5,7 @@ require "fileutils"
5
5
  require "yaml"
6
6
 
7
7
  Hiiro.load_env
8
- hiiro = Hiiro.init(*ARGV, plugins: [Task])
8
+ hiiro = Hiiro.init(*ARGV, plugins: [OldTask])
9
9
 
10
10
  class BranchManager
11
11
  attr_reader :hiiro
@@ -8,7 +8,7 @@ require "yaml"
8
8
  require "time"
9
9
 
10
10
  Hiiro.load_env
11
- hiiro = Hiiro.init(*ARGV, plugins: [Tmux, Task])
11
+ hiiro = Hiiro.init(*ARGV, plugins: [Tmux, OldTask])
12
12
 
13
13
  tasks = hiiro.task_manager
14
14
 
@@ -16,7 +16,7 @@ hiiro.add_subcmd(:edit) { system(ENV['EDITOR'] || 'nvim', __FILE__) }
16
16
  hiiro.add_subcmd(:list) { tasks.list_subtasks }
17
17
  hiiro.add_subcmd(:ls) { tasks.list_subtasks }
18
18
  hiiro.add_subcmd(:new) { |subtask_name| tasks.new_subtask(subtask_name) }
19
- hiiro.add_subcmd(:switch) { |subtask_name| tasks.switch_subtask(subtask_name) }
19
+ hiiro.add_subcmd(:switch) { |subtask_name=nil| tasks.switch_subtask(subtask_name) }
20
20
  hiiro.add_subcmd(:app) { |app_name| tasks.subtask_open_app(app_name) }
21
21
  hiiro.add_subcmd(:path) { |app_name=nil| tasks.subtask_app_path(app_name) }
22
22
  hiiro.add_subcmd(:cd) { |*args| tasks.subtask_cd_app(*args) }
@@ -44,23 +44,55 @@ class TaskManager
44
44
  active, available = trees.partition { |tree_name| task_for_tree(tree_name) }
45
45
  sessions = tmux_sessions
46
46
 
47
+ # Group active trees by parent task
48
+ groups = {}
47
49
  active.each do |tree_name|
48
50
  task = task_for_tree(tree_name)
49
- marker = (current && current[:tree] == tree_name) ? "*" : " "
50
-
51
- # Check if there's a tmux session for this task
52
- session_name = session_name_for(task)
53
- has_session = sessions.include?(session_name)
54
- session_marker = has_session ? "+" : " "
55
- session_info = has_session ? " (#{session_name})" : ""
56
-
57
- puts format("%s%s %-20s => %s%s", marker, session_marker, tree_name, task, session_info)
51
+ parent = task.include?('/') ? task.split('/').first : task
52
+ groups[parent] ||= []
53
+ groups[parent] << { tree: tree_name, task: task }
54
+ end
55
+
56
+ first_group = true
57
+ groups.each do |parent, entries|
58
+ puts unless first_group
59
+ first_group = false
60
+
61
+ # Sort so the main entry (parent itself or parent/main) comes first
62
+ entries.sort_by! { |e| e[:task] == parent || e[:task].end_with?('/main') ? 0 : 1 }
63
+
64
+ entries.each_with_index do |entry, i|
65
+ tree_name = entry[:tree]
66
+ task = entry[:task]
67
+ marker = (current && current[:tree] == tree_name) ? "*" : " "
68
+ branch = worktree_branch(tree_name)
69
+ branch_str = branch ? " [#{branch}]" : ""
70
+
71
+ # Check if there's a tmux session for this task
72
+ session_name = session_name_for(task)
73
+ has_session = sessions.include?(session_name)
74
+ session_marker = has_session ? "+" : " "
75
+
76
+ if i == 0
77
+ # Parent task line
78
+ display_name = parent
79
+ puts format("%s%s %s%s", marker, session_marker, display_name, branch_str)
80
+ else
81
+ # Subtask line: align /child_name under the parent name
82
+ child_name = task.include?('/') ? task.split('/', 2).last : task
83
+ padding = " " * parent.length
84
+ puts format("%s%s %s/%s%s", marker, session_marker, padding, child_name, branch_str)
85
+ end
86
+ end
58
87
  end
59
88
 
60
- puts if active.any? && available.any?
61
-
62
- available.each do |tree_name|
63
- puts format(" %-20s (available)", tree_name)
89
+ if available.any?
90
+ puts
91
+ available.each do |tree_name|
92
+ branch = worktree_branch(tree_name)
93
+ branch_str = branch ? " [#{branch}]" : ""
94
+ puts format(" %-20s (available)%s", tree_name, branch_str)
95
+ end
64
96
  end
65
97
 
66
98
  # List tmux sessions without associated tasks
@@ -415,11 +447,11 @@ class TaskManager
415
447
  worktree_info.keys.sort
416
448
  end
417
449
 
418
- # Parse git worktree list output into { name => path } hash
419
- def worktree_info
420
- @worktree_info ||= begin
450
+ # Parse git worktree list output into { name => { path:, branch: } } hash
451
+ def worktree_details
452
+ @worktree_details ||= begin
421
453
  output = `git -C #{main_repo_path} worktree list --porcelain 2>/dev/null`
422
- info = {}
454
+ details = {}
423
455
  current_path = nil
424
456
 
425
457
  work_dir = File.join(Dir.home, 'work')
@@ -433,6 +465,12 @@ class TaskManager
433
465
  # Skip bare repo
434
466
  current_path = nil
435
467
  elsif line.start_with?('branch ') || line == 'detached'
468
+ branch = if line.start_with?('branch ')
469
+ line.sub('branch refs/heads/', '')
470
+ else
471
+ '(detached)'
472
+ end
473
+
436
474
  # Capture worktree (both named branches and detached HEAD)
437
475
  if current_path && current_path != main_repo_path
438
476
  # Use relative path from ~/work/ to support nested worktrees
@@ -442,17 +480,28 @@ class TaskManager
442
480
  else
443
481
  File.basename(current_path)
444
482
  end
445
- info[name] = current_path
483
+ details[name] = { path: current_path, branch: branch }
446
484
  end
447
485
  current_path = nil
448
486
  end
449
487
  end
450
488
 
451
- info
489
+ details
452
490
  end
453
491
  end
454
492
 
493
+ # Backward-compatible { name => path } hash
494
+ def worktree_info
495
+ @worktree_info ||= worktree_details.transform_values { |v| v[:path] }
496
+ end
497
+
498
+ # Get branch name for a worktree
499
+ def worktree_branch(tree_name)
500
+ worktree_details.dig(tree_name, :branch)
501
+ end
502
+
455
503
  def clear_worktree_cache
504
+ @worktree_details = nil
456
505
  @worktree_info = nil
457
506
  end
458
507
 
data/bin/h-pr CHANGED
@@ -7,7 +7,7 @@ require "yaml"
7
7
  require "json"
8
8
 
9
9
  Hiiro.load_env
10
- hiiro = Hiiro.init(*ARGV, plugins: [Task, Tmux, Pins])
10
+ hiiro = Hiiro.init(*ARGV, plugins: [OldTask, Tmux, Pins])
11
11
 
12
12
  class PRManager
13
13
  attr_reader :hiiro
data/exe/h CHANGED
@@ -3,7 +3,7 @@
3
3
  require "hiiro"
4
4
  require "fileutils"
5
5
 
6
- hiiro = Hiiro.init(*ARGV, cwd: Dir.pwd)
6
+ hiiro = Hiiro.init(*ARGV, cwd: Dir.pwd, plugins: [Tasks])
7
7
 
8
8
  hiiro.add_subcommand(:version) { |*args|
9
9
  puts Hiiro::VERSION
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.30"
2
+ VERSION = "0.1.32"
3
3
  end
data/notes ADDED
@@ -0,0 +1,224 @@
1
+
2
+ # Principles to use for refactoring
3
+
4
+ - Separate data from behavior - create domain objects and any relevant value objects
5
+
6
+
7
+ # Goals
8
+
9
+ - same code to manage tasks/subtasks
10
+ - just scoped differently
11
+ - if scope is `:task` then consider all tasks, if scope is :subtask then only consider the tasks associated with the parent task
12
+ - extract data objects
13
+ - so our procedures are human and easy af
14
+ - more composable
15
+ - separate presentation layer from the data
16
+
17
+
18
+ # features we want
19
+
20
+ - list tasks/subtasks
21
+ - start a new one (wtree/tmux session)
22
+ - switch between them
23
+ - cd to an app's directory
24
+ - stop a task
25
+ - use yml files to track things
26
+
27
+ turn @plugins/tasks.rb into a plugin similar to @plugins/task.rb
28
+
29
+ but in add_subcommands(hiiro) i want to define 2 subtasks:
30
+ 1. `:task`
31
+ 2. `:subtask`
32
+
33
+ both subcommands should initialize `Tasks.new(hiiro_instance, :task)` or
34
+ `Tasks.new(hiiro_instance, :subtask)` if the subtask subcmd was called
35
+
36
+ then uses the instance of Tasks to either do all the things that it needs to
37
+ do... or just use it to get the data it needs, so that it can do the high level
38
+ things like running tmux or git worktree commands.
39
+ if it doesn't put those things inside Tasks, that would be better, so I can use
40
+ these objects in other places like inside @bin/h-link
41
+
42
+ use the following interface as a guide for how to organize things inside the
43
+ new Tasks plugin
44
+
45
+ # Interface
46
+
47
+ class Environment
48
+ def self.current # => Environment - makes new instance
49
+ def all_tasks # => Task[] - call Task.all and memo cache the result
50
+ def all_sessions # => Session[] - call TmuxSession.all and memo cache the result
51
+ def all_trees # => Tree[] - call Tree.all and memo cache the result
52
+ def task # => Task - move Task.current's behavior into this method, and memo cache the result
53
+ def session # => Session - move TmuxSession.current here and memo cache the result
54
+ def tree # => Tree - move Tree.current here and memo cache the result
55
+
56
+ class Tree
57
+ attr_reader :path, :task, :session
58
+
59
+ def self.current # => Tree - move to Environment#tree
60
+ def self.all # => Tree[]
61
+
62
+ class Session
63
+ attr_reader :name, :task
64
+
65
+ def self.current # => Session - move to Environment#session
66
+ def self.all # => Session[]
67
+
68
+ class Task
69
+ attr_reader :name, :tree, :session, :parent, :subtasks
70
+
71
+ def self.current # => Task - move to Environment#task
72
+ def self.all # => Task[]
73
+
74
+ class App
75
+ attr_reader :name, :path
76
+ def self.current # => App[]
77
+ def self.all # => App[]
78
+
79
+ class Tasks
80
+ attr_reader :hiiro, :scope, :environment
81
+
82
+ def tasks # => Task[]
83
+ def subtasks(task) # => Task[]
84
+ def task_by_name(name) # => Task
85
+ def task_by_tree(tree_name) # => Task
86
+ def task_by_session(session_name) # => Task
87
+ def current_task # => Task
88
+ def current_session # => Session
89
+ def current_tree # => Tree
90
+
91
+ def start_task(name, path) # => Task
92
+ def switch_to_task(task) # => nil
93
+ def stop_task(task) # => nil
94
+ def list(tasks) # => nil # print to screen
95
+ def cd_to_task(task) # => nil
96
+ def cd_to_app(app_name) # => nil
97
+
98
+ class Config
99
+ attr_reader :path
100
+
101
+
102
+
103
+
104
+ # Follow-up prompts
105
+
106
+ ## i forget
107
+
108
+ > when initializing an instance of `Task`...it shouldn't have a `parent_task` or `parent_name` kwarg. it derive that context from the name given. if the name has a `/` in it...then it
109
+ is a subtask and the left side is the parent name and the right side is the subtask name.
110
+
111
+
112
+ ## lots of updates
113
+
114
+ here are some updates i'd like to make:
115
+
116
+ - all arguments received from the user should looking that object using
117
+ - something like `start_with?` so they can abbreviate the names of things to
118
+ match
119
+ - the only special one is task_name or subtask_name...if there is a `/` the
120
+ left side will do the abbreviation lookup on a parent name and the right
121
+ side will do a lookup on the subtask name
122
+ - you might want to add `#find_tree(abbreviated_name)` method to
123
+ Environment
124
+ - you might want to add `#find_session(abbreviated_name)` method to
125
+ Environment
126
+ - you might want to add `#find_app(abbreviated_name)` method to
127
+ Environment
128
+ - you might want to add `#find_task(abbreviated_name)` method to
129
+ Environment, where it does the left/right abbreviation lookups
130
+ - these new methods should help clean up and simplify all the lookups in
131
+ the other methods that typically use `.find{ ... }`
132
+
133
+ - get rid of methods like `apps_hash` and in the places that used it...
134
+ - just use the array returned from `#apps` and the attr_reader methods
135
+ for things like name and path or other values that there are attr_readers
136
+ for.
137
+ - always favor an instance method or attr_reader over exporting something
138
+ like hash
139
+
140
+ - `#start_task` shouldn't take an optional `tree_path` keyword arg
141
+ - instead it should take an optional `app_name` kwarg and set the tmux
142
+ session's base directory to the matching app's configured directory
143
+
144
+ - `#switch_task` should also take an optional `app_name` kwarg. if a tmux
145
+ session already exists, just update the configs, associating the task
146
+ with that app. if the tmux session doesn't exist, use the app's path as the
147
+ base_directory.
148
+
149
+
150
+ here's one fix i really want to get right.
151
+
152
+ right now TasksPlugin has a dispatch method that uses a case statement to
153
+ handle the subcommands. what i'd like instead is for tasks plugin to initialize
154
+ a new instance of `Hiiro` but instead of it receiving it's usual `*ARGV` as
155
+ positional args, you should pass-in t `*hiiro.args` using the existing `hiiro`
156
+ instance. then for the new `Hiiro` instance...before you call `#run` on
157
+ it...add each of the subcommands defined in the case statement using the
158
+ `add_subcmd` method and have the block passed in be the handler for those
159
+ subcommands
160
+
161
+ so something like this:
162
+
163
+ ```ruby
164
+ module TasksPlugin
165
+ # ...
166
+
167
+ def self.add_subcommands(hiiro)
168
+ hiiro.add_subcmd(:task) do |*args|
169
+ mgr = Tasks.new(hiiro, scope: :task)
170
+ new_hiiro = task_hiiro(hiiro, mgr)
171
+ new_hiiro.run
172
+ end
173
+
174
+ hiiro.add_subcmd(:subtask) do |*args|
175
+ mgr = Tasks.new(hiiro, scope: :subtask)
176
+ new_hiiro = task_hiiro(hiiro, mgr)
177
+ new_hiiro.run
178
+ end
179
+ end
180
+
181
+ def self.task_hiiro(parent_hiiro, mgr)
182
+ Hiiro.init(*parent_hiiro.args, mgr: mgr) do |task_hiiro|
183
+ task_hiiro.add_subcmd(:ls) do |*args|
184
+ # TODO: the logic to print out the task list based on mgr's values
185
+ end
186
+
187
+ task_hiiro.add_subcmd(:start) do |task_name, app_name=nil|
188
+ # TODO: the logic to print out the task list based on mgr's values
189
+ # like:
190
+ mgr.start_task(task_name, app_name: app_name)
191
+ end
192
+
193
+ task_hiiro.add_subcmd(:switch) do |task_name, app_name=nil|
194
+ # TODO: the logic to print out the task list based on mgr's values
195
+ mgr.switch_to_task(task_name, app_name: app_name)
196
+
197
+ # but if you need to add logic, i would do it here...like:
198
+ task = mgr.find_task(task_name)
199
+
200
+ if task
201
+ # run cmds to switch to task here...
202
+ else
203
+ # print some msg like no task found
204
+ end
205
+ end
206
+
207
+ # ...
208
+ end
209
+ end
210
+
211
+ # ...
212
+ end
213
+ ```
214
+
215
+ ## third prompt
216
+
217
+ for things like listing and switching subtasks
218
+ always consider the main/parent task as a potential subtask to switch to.
219
+ for lookups, treat the task name as parent name and `main` as the subtask name
220
+ so something like `nameofparent/main`
221
+
222
+
223
+
224
+