hiiro 0.1.370 → 0.1.371

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: 032503df05064bda5cc037dd3cd37eccea64c836264d08f40f9a23455d08f8a6
4
- data.tar.gz: e9b478550d879b3b205e0ca9f04d442782a94c777116eb06901e726c21e785c7
3
+ metadata.gz: 4910c0070a20706820a27abe45f728d8751da4d9d2297cacd05d26836c96dce1
4
+ data.tar.gz: 3173f0e134ef9353383405aff0dfd72ebe502a72ec3a2f149000dd6d221e6116
5
5
  SHA512:
6
- metadata.gz: 31f274880fc13eb3448a7970030c78e5d86a1807aff1bdd0ad66f2ffa9c87076f59c174f9d9ac74400a1fc773ad866a5e798f4d3f19e2d977c949ec59ba3514e
7
- data.tar.gz: 931992993a1505231ae81781a3d8707c40d94c762c3902484c1a1357a5fe02240c616374d64e51622db5be11bae0abd7fc40114f0c994ebd4b4cf18669dd304b
6
+ metadata.gz: e0fb10ffa72f026a25c0cba9ebe6f926f7d6c7504ea1d51f729519feed61918cba7fb2cf0da814c322f787c90ec532b667ea22c9e2c3bbfacb414758fb30cbc8
7
+ data.tar.gz: 5192581c3c17c73e5d7781f3cfd736e16678d89be171ca1676dfedd7e49c53e007bef1efe5b1f80bf61e827113fad82b07206564ce9779ccc97a1cbec28f4449
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.1.371] - 2026-09-14
6
+
7
+ ### Added
8
+ - `t ls` lists the panes under every open workspace with directory, agent state, and foreground command via `herdr pane process-info`; `t switch` accepts an exact pane ID and its picker includes panes
9
+ - `Hiiro::Herdr#process_info` and `Pane#foreground_command`
10
+
5
11
  ## [0.1.370] - 2026-09-14
6
12
 
7
13
  ### Added
data/docs/t.md CHANGED
@@ -29,7 +29,7 @@ Task names may be `parent/child` for subtasks. `t new NAME` creates exactly `NAM
29
29
 
30
30
  | Command | Behavior |
31
31
  |---|---|
32
- | `t`, `t ls`, `t list` | Lists all tasks alphabetically with open todo counts, e.g. `prez (3)`, plus `next:` and `waiting:` text. When Herdr is running, tasks whose workspace is open get an `@` marker, and live workspaces that belong to no task are listed afterwards with their IDs |
32
+ | `t`, `t ls`, `t list` | Lists all tasks alphabetically with open todo counts, e.g. `prez (3)`, plus `next:` and `waiting:` text. When Herdr is running, tasks whose workspace is open get an `@` marker, every open workspace lists its panes as `ID ~/dir agent (status) command` (from `herdr pane process-info`), and live workspaces that belong to no task are listed afterwards with their IDs |
33
33
  | `t show [TASK]` | Status, home, next action, waiting text, code directory, workspace label, resources, documents, and todos |
34
34
  | `t current [TASK]` | Prints the resolved task name; never saves |
35
35
  | `t use TASK` (alias `pin`) | Saves TASK as the fallback for when no workspace or directory identifies a task |
@@ -112,7 +112,7 @@ t pane run [TASK] ID|LABEL [--] COMMAND...
112
112
  t pane split [TASK] ID|LABEL [--direction right|down] [--directory PATH] [--command COMMAND]
113
113
  ```
114
114
 
115
- These require a running Herdr server. The workspace label is the task session or name with `.` replaced by `_`. `switch` also accepts the name of a live Herdr workspace that belongs to no task, exactly or by unique prefix, and focuses it; a task with the same name wins. When the name is ambiguous, or no task or context is given, a terminal gets a fuzzy finder over tasks and loose workspaces, with duplicate workspace names numbered in the label only. `switch` focuses the existing task workspace or creates it in the start directory (`--directory`, else primary directory, worktree, or home), and saves the task as the fallback when it was named explicitly. `--show` only prints the workspace, tabs, and panes. Tab and pane references match a live ID or label, then a unique prefix; with no reference and several candidates a fuzzy finder opens.
115
+ These require a running Herdr server. The workspace label is the task session or name with `.` replaced by `_`. `switch` also accepts the name of a live Herdr workspace that belongs to no task, exactly or by unique prefix, and focuses it; a task with the same name wins. An exact pane ID such as `w6:p2` focuses that pane, and the picker lists every live pane with its directory and foreground command. When the name is ambiguous, or no task or context is given, a terminal gets a fuzzy finder over tasks and loose workspaces, with duplicate workspace names numbered in the label only. `switch` focuses the existing task workspace or creates it in the start directory (`--directory`, else primary directory, worktree, or home), and saves the task as the fallback when it was named explicitly. `--show` only prints the workspace, tabs, and panes. Tab and pane references match a live ID or label, then a unique prefix; with no reference and several candidates a fuzzy finder opens.
116
116
 
117
117
  ## Native AI sessions
118
118
 
data/lib/hiiro/herdr.rb CHANGED
@@ -204,6 +204,19 @@ class Hiiro
204
204
  def to_s
205
205
  "#{id} #{name} [#{workspace_id} / #{tab_id}]#{focused? ? ' *' : ''}"
206
206
  end
207
+
208
+ # `herdr pane process-info` for this pane, fetched once.
209
+ def process_info
210
+ @process_info ||= client.process_info(id)
211
+ end
212
+
213
+ # Command line of the foreground process group leader, or nil at a shell prompt.
214
+ def foreground_command
215
+ procs = process_info['foreground_processes'] || []
216
+ leader = procs.find { |proc| proc['pid'] == process_info['foreground_process_group_id'] } || procs.last
217
+ return nil if leader.nil? || leader['pid'] == process_info['shell_pid']
218
+ leader['cmdline']
219
+ end
207
220
  end
208
221
 
209
222
  class Panes < Collection
@@ -319,6 +332,10 @@ class Hiiro
319
332
  row && Tab.new(row, client: self)
320
333
  end
321
334
 
335
+ def process_info(pane_id)
336
+ capture_result('pane', 'process-info', '--pane', pane_id).fetch('process_info', {})
337
+ end
338
+
322
339
  def get_pane(id)
323
340
  row = capture_result('pane', 'get', id)['pane']
324
341
  row && Pane.new(row, client: self, rect: pane_rect(id))
@@ -186,8 +186,31 @@ class Hiiro
186
186
  []
187
187
  end
188
188
 
189
+ # All live panes grouped by workspace id, or {} when Herdr is not running.
190
+ def live_panes
191
+ @live_panes ||= begin
192
+ herdr_client.server_running? ? client.panes(all: true).to_a.group_by(&:workspace_id) : {}
193
+ rescue Hiiro::Error
194
+ {}
195
+ end
196
+ end
197
+
198
+ # "ID ~/dir agent (status) command line" for listings and pickers.
199
+ def pane_line(pane)
200
+ dir = (pane.foreground_cwd || pane.cwd).to_s.sub(/\A#{Regexp.escape(Dir.home)}(?=\/|\z)/, '~')
201
+ what = []
202
+ what << "#{pane.agent}#{pane.agent_status ? " (#{pane.agent_status})" : ''}" if pane.agent
203
+ what << (pane.foreground_command || (pane.agent ? nil : 'shell'))
204
+ [pane.id, dir, *what.compact].reject(&:empty?).join(' ')
205
+ end
206
+
207
+ def print_panes(workspace_id, indent: ' ')
208
+ live_panes.fetch(workspace_id, []).each { |pane| puts "#{indent}#{pane_line(pane)}" }
209
+ end
210
+
189
211
  # Tasks with open todo counts, an @ marker when the task workspace is open,
190
- # then any live Herdr workspaces that belong to no task.
212
+ # then any live Herdr workspaces that belong to no task. Panes are listed
213
+ # under every open workspace with their directory and foreground command.
191
214
  def list_tasks
192
215
  tasks = Hiiro::TaskRecord.all_as_list
193
216
  workspaces = live_workspaces
@@ -211,11 +234,18 @@ class Hiiro
211
234
  end
212
235
  name_col = rows.map { |row| row[0].length }.max || 0
213
236
  status_col = rows.map { |row| row[1].length }.max || 0
214
- rows.each { |label, status, detail| puts format("%-#{name_col}s %-#{status_col}s %s", label, status, detail).rstrip }
237
+ rows.each_with_index do |(label, status, detail), index|
238
+ puts format("%-#{name_col}s %-#{status_col}s %s", label, status, detail).rstrip
239
+ workspace = workspaces.find { |candidate| candidate.name == workspace_label(tasks[index]) }
240
+ print_panes(workspace.id) if workspace
241
+ end
215
242
  return if loose.empty?
216
243
  puts unless rows.empty?
217
244
  puts 'Workspaces without a task:'
218
- loose.each { |workspace| puts " #{workspace.name} #{workspace.id}" }
245
+ loose.each do |workspace|
246
+ puts " #{workspace.name} #{workspace.id}"
247
+ print_panes(workspace.id)
248
+ end
219
249
  end
220
250
 
221
251
 
@@ -485,10 +515,11 @@ class Hiiro
485
515
  client.workspaces.reject { |workspace| labels.include?(workspace.name) }
486
516
  end
487
517
 
488
- # Fuzzy map over tasks and loose workspaces; duplicate workspace names are numbered
489
- # in the label only, so the choice still maps to the exact workspace.
490
- def pick_switch_target(tasks, workspaces)
491
- raise Error, 'No tasks or workspaces to choose from' if tasks.empty? && workspaces.empty?
518
+ # Fuzzy map over tasks, loose workspaces, and live panes; duplicate workspace
519
+ # names are numbered in the label only, so the choice still maps to the exact one.
520
+ def pick_switch_target(tasks, workspaces, panes: nil)
521
+ panes = live_panes.values.flatten if panes.nil?
522
+ raise Error, 'No tasks, workspaces, or panes to choose from' if tasks.empty? && workspaces.empty? && panes.empty?
492
523
  counts = workspaces.group_by(&:name).transform_values(&:length)
493
524
  seen = Hash.new(0)
494
525
  map = tasks.to_h { |task| [task.name, task] }
@@ -497,9 +528,16 @@ class Hiiro
497
528
  label = counts[workspace.name] > 1 ? "#{workspace.name} ##{seen[workspace.name]}" : workspace.name
498
529
  map["#{label} (workspace)"] = workspace
499
530
  end
531
+ panes.each { |pane| map["#{pane_line(pane)} (pane)"] = pane }
500
532
  fuzzyfind_from_map(map) || raise(Error, 'Nothing selected')
501
533
  end
502
534
 
535
+ def focus_pane_target(pane)
536
+ check_result(client.focus_workspace(pane.workspace_id))
537
+ check_result(client.focus_pane(pane.id), "Could not focus pane #{pane.id}")
538
+ puts pane_line(pane)
539
+ end
540
+
503
541
  # [target, explicit]. A task wins over a workspace with the same name.
504
542
  def switch_target(args)
505
543
  args = args.dup
@@ -513,6 +551,8 @@ class Hiiro
513
551
  workspaces = loose_workspaces
514
552
  exact = tasks.find { |task| task.name == first }
515
553
  return [exact, true] if exact
554
+ pane = live_panes.values.flatten.find { |candidate| candidate.id == first }
555
+ return [pane, false] if pane
516
556
  exact_ws = workspaces.select { |workspace| workspace.name == first }
517
557
  return [exact_ws.first, false] if exact_ws.one?
518
558
  task_matches = tasks.select { |task| task.name.start_with?(first) }
@@ -522,7 +562,7 @@ class Hiiro
522
562
  raise Error, "No task or workspace matches #{first}" if task_matches.empty? && ws_matches.empty?
523
563
  names = (task_matches.map(&:name) + ws_matches.map { |w| "#{w.name} (workspace)" }).join(', ')
524
564
  raise Error, "Ambiguous #{first}: #{names}" unless $stdin.tty?
525
- target = pick_switch_target(task_matches, ws_matches)
565
+ target = pick_switch_target(task_matches, ws_matches, panes: [])
526
566
  return [target, target.is_a?(Hiiro::TaskRecord)]
527
567
  end
528
568
  args.shift if first == '.'
@@ -756,7 +796,10 @@ class Hiiro
756
796
  add_option :directory, desc: 'Existing start directory'
757
797
  add_cmd(:switch, :workspace, args: ['task-or-workspace?'], opts: [*TASK_OPTIONS, :directory, :show]) do
758
798
  target, explicit = switch_target(opts.args)
759
- if target.is_a?(Hiiro::Herdr::Workspace)
799
+ if target.is_a?(Hiiro::Herdr::Pane)
800
+ raise Error, '--show applies to task workspaces' if opts.show
801
+ focus_pane_target(target)
802
+ elsif target.is_a?(Hiiro::Herdr::Workspace)
760
803
  raise Error, '--show applies to task workspaces' if opts.show
761
804
  check_result(client.focus_workspace(target.id))
762
805
  puts target
data/lib/hiiro/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  class Hiiro
2
- VERSION = "0.1.370"
2
+ VERSION = "0.1.371"
3
3
  SUPPORTED_RUBY_VERSION = ">= 3.2.0"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiiro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.370
4
+ version: 0.1.371
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota