hiiro 0.1.368 → 0.1.369

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: 975c627734daa28e7efa5c45802cd3f296ec3f338c7e2ad73835e22d219223e6
4
- data.tar.gz: bcd42f9f3b2ca7bbaf33b67e1535aa4729d3701f353d313bfe3a7f64c27b3e19
3
+ metadata.gz: 3565b794ff2dd7e61d45810479a5bed58621c54ac4e3afa51f12adeb9626c639
4
+ data.tar.gz: 75073d800fee5ab0185eea6062b39961b7f68c4200a1f428f8ac72f090edabd5
5
5
  SHA512:
6
- metadata.gz: 22b19c645b77356da44007ab866f00f769ef875444b9ad5b4d00ae080ff5797b3b4cc3c500b65118ba3801f025c061f8ef01bb885aff7a63692c83c3b1ee09cc
7
- data.tar.gz: aad14264c543596424e10c301401e618e13adbef8dd7e6088b9d1b6c3e9374a357040d2886843057c290f0dda65a6f6100992bfbf3bdd3d1049eb914984ea8c5
6
+ metadata.gz: 9435184f96a4b71ea43ecb873180411326aaf5ebb866153748be8f9e5fdbe7605d92c71df4506498029772f510374e91034ce74748663a9d1b4de8ff264d5c17
7
+ data.tar.gz: 8a8a5539086877c051ce05639fdb2c72a626c88d92284981c551fff2a3b6a8d6e048250d969b6fedb4e811c720da0a4c58a1e6aa97dddebeba1a478f1c26194e
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.1.369] - 2026-09-14
6
+
7
+ ### Added
8
+ - `t switch NAME` matches live Herdr workspaces that belong to no task, and the fuzzy finder for `switch` lists tasks plus those workspaces, numbering duplicate workspace names
9
+ - `t` opens the fuzzy finder instead of failing when a task prefix is ambiguous or no current task can be resolved, when stdin is a terminal
10
+
5
11
  ## [0.1.368] - 2026-09-14
6
12
 
7
13
  ### Added
@@ -97,4 +103,4 @@
97
103
  - Focus the exact Herdr pane through the socket API and read the CLI's plain-text pane output without JSON parsing.
98
104
  - Restore the missing `Hiiro::Bins` helper so `require "hiiro"` boots and commands like `h jumplist record` dispatch correctly.
99
105
  - Make Hiiro's Ruby requirement explicit as Ruby 3.2+ and have rbenv-wide gem installs skip incompatible Ruby versions.
100
- - Update the publish script to preserve the Ruby support constant, run only on supported Ruby, and install releases only into compatible rbenv versions.
106
+ - Update the publish script to preserve the Ruby support constant, run only on supported Ruby, and install releases only into compatible rbenv versions.
data/README.md CHANGED
@@ -73,7 +73,8 @@ Commands that act on a task take it from the first positional argument when
73
73
  that word names a task (exact or unique case-sensitive prefix); otherwise the
74
74
  current task is used and the word stays in the payload, exactly like the old
75
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.
76
+ `.` is the current task, and `-` selects orphan todos. On a terminal, an ambiguous
77
+ prefix or a missing current task opens the fuzzy finder; non-interactive runs fail.
77
78
 
78
79
  The current task is the calling Herdr workspace, then the current directory
79
80
  inside a task home, code directory, worktree, or registered directory, then the
data/docs/t.md CHANGED
@@ -17,11 +17,11 @@ The command comes first. Commands that act on a task take it from the first posi
17
17
 
18
18
  1. If the first positional word names a task, exactly or by a unique case-sensitive prefix, it is the task and is removed from the arguments.
19
19
  2. Otherwise the current task is used and the word stays in the payload. So `t todo add fix build` adds to the current task and `t todo add prez fix build` adds to `prez`.
20
- 3. An ambiguous prefix is an error rather than a guess.
20
+ 3. An ambiguous prefix opens the fuzzy finder over the matching tasks when stdin is a terminal, and is an error otherwise.
21
21
 
22
22
  `-t TASK` / `--task TASK` forces a task and fails if it does not exist. `-f` / `--find` picks one with `sk` or `fzf`. `.` is the current task, useful when a payload could be mistaken for a name: `t next . Finish slides`. `-` selects orphan todos in todo commands only. Words starting with `-` are never treated as task names. Passthrough commands (`todo add`, `sh`, `pane run`, and the AI launchers) accept `-t`, `--task=NAME`, and `-f` only as their leading arguments.
23
23
 
24
- The current task is resolved by `Hiiro::CurrentTask`: the calling Herdr workspace when `HERDR_*` variables identify one, then the working directory inside a task home, primary directory, worktree, or registered directory, then the task saved by `t use`. Ambiguous matches and stale or conflicting Herdr IDs are errors. Commands that only list, create, or show help never resolve the current task.
24
+ The current task is resolved by `Hiiro::CurrentTask`: the calling Herdr workspace when `HERDR_*` variables identify one, then the working directory inside a task home, primary directory, worktree, or registered directory, then the task saved by `t use`. With no context at all, a terminal gets the fuzzy finder over every task; non-interactive runs fail. Ambiguous directory or workspace matches and stale or conflicting Herdr IDs are always errors. Commands that only list, create, or show help never resolve the current task.
25
25
 
26
26
  Task names may be `parent/child` for subtasks. `t new NAME` creates exactly `NAME`; nothing else creates tasks. Only `help`, `ls`, and `list` are reserved words; a task literally named `show` or `new` is reachable with `-t show`.
27
27
 
@@ -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` focuses the existing 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. 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
 
@@ -40,24 +40,37 @@ class Hiiro
40
40
  add_flag :find, short: :f, desc: 'Choose the task with a fuzzy finder'
41
41
  end
42
42
 
43
- def task_names
44
- Hiiro::TaskRecord.all_as_list.map(&:name)
45
- end
46
-
47
43
  # Exact name, else unique prefix; ambiguous prefixes fail; nil when nothing matches.
44
+ # Exact name, else unique prefix. An ambiguous prefix opens the fuzzy finder
45
+ # over the candidates on a TTY and fails otherwise. nil when nothing matches.
48
46
  def lookup_task(reference)
49
47
  result = Hiiro::Matcher.new(Hiiro::TaskRecord.all_as_list, :name).by_prefix(reference.to_s)
50
48
  match = result.resolved
51
- raise Error, "Ambiguous task #{reference}: #{result.matches.map { |m| m.item.name }.join(', ')}" if !match && result.ambiguous?
52
- match&.item
49
+ return match.item if match
50
+ return nil unless result.ambiguous?
51
+ candidates = result.matches.map(&:item)
52
+ raise Error, "Ambiguous task #{reference}: #{candidates.map(&:name).join(', ')}" unless $stdin.tty?
53
+ pick_task(candidates)
54
+ end
55
+
56
+ def pick_task(candidates)
57
+ raise Error, 'No tasks; create one with t new NAME' if candidates.empty?
58
+ name = fuzzyfind(candidates.map(&:name))
59
+ raise Error, 'No task selected' if name.nil? || name.strip.empty?
60
+ candidates.find { |task| task.name == name.strip } || raise(Error, "No task named #{name.strip}")
53
61
  end
54
62
 
55
63
  def lookup_task!(reference)
56
64
  lookup_task(reference) || raise(Error, "Task not found: #{reference}; run t new #{reference}")
57
65
  end
58
66
 
67
+ # Current task from Herdr, directory, or the saved pin. With no context at all,
68
+ # a TTY gets the fuzzy finder over every task; ambiguous or stale context still fails.
59
69
  def current_task
60
70
  Hiiro::CurrentTask.new(herdr: -> { herdr_client }, pin: true).resolve!
71
+ rescue Hiiro::Error => e
72
+ raise unless $stdin.tty? && e.message.start_with?('No current task')
73
+ pick_task(Hiiro::TaskRecord.all_as_list)
61
74
  end
62
75
 
63
76
  # Old h task rule: [task, remaining_args]. The first positional is the task
@@ -80,11 +93,7 @@ class Hiiro
80
93
  end
81
94
  end
82
95
  return [lookup_task!(forced), args] if forced
83
- if find
84
- name = fuzzyfind(task_names)
85
- raise Error, 'No task selected' if name.nil? || name.strip.empty?
86
- return [lookup_task!(name.strip), args]
87
- end
96
+ return [pick_task(Hiiro::TaskRecord.all_as_list), args] if find
88
97
  first = args.first
89
98
  if first == '.'
90
99
  args.shift
@@ -450,6 +459,65 @@ class Hiiro
450
459
  puts workspace
451
460
  end
452
461
 
462
+ # --- switch targets: tasks plus live Herdr workspaces that are not task workspaces ---
463
+
464
+ def loose_workspaces
465
+ return [] unless herdr_client.server_running?
466
+ labels = Hiiro::TaskRecord.all_as_list.map { |task| workspace_label(task) }
467
+ client.workspaces.reject { |workspace| labels.include?(workspace.name) }
468
+ end
469
+
470
+ # Fuzzy map over tasks and loose workspaces; duplicate workspace names are numbered
471
+ # in the label only, so the choice still maps to the exact workspace.
472
+ def pick_switch_target(tasks, workspaces)
473
+ raise Error, 'No tasks or workspaces to choose from' if tasks.empty? && workspaces.empty?
474
+ counts = workspaces.group_by(&:name).transform_values(&:length)
475
+ seen = Hash.new(0)
476
+ map = tasks.to_h { |task| [task.name, task] }
477
+ workspaces.each do |workspace|
478
+ seen[workspace.name] += 1
479
+ label = counts[workspace.name] > 1 ? "#{workspace.name} ##{seen[workspace.name]}" : workspace.name
480
+ map["#{label} (workspace)"] = workspace
481
+ end
482
+ fuzzyfind_from_map(map) || raise(Error, 'Nothing selected')
483
+ end
484
+
485
+ # [target, explicit]. A task wins over a workspace with the same name.
486
+ def switch_target(args)
487
+ args = args.dup
488
+ return [lookup_task!(opts.task), true] if opts.task
489
+ return [pick_switch_target(Hiiro::TaskRecord.all_as_list, loose_workspaces), true] if opts.find
490
+ first = args.first
491
+ if first && first != '.' && !first.start_with?('-')
492
+ args.shift
493
+ no_args!(args)
494
+ tasks = Hiiro::TaskRecord.all_as_list
495
+ workspaces = loose_workspaces
496
+ exact = tasks.find { |task| task.name == first }
497
+ return [exact, true] if exact
498
+ exact_ws = workspaces.select { |workspace| workspace.name == first }
499
+ return [exact_ws.first, false] if exact_ws.one?
500
+ task_matches = tasks.select { |task| task.name.start_with?(first) }
501
+ ws_matches = exact_ws.any? ? exact_ws : workspaces.select { |workspace| workspace.name.start_with?(first) }
502
+ return [task_matches.first, true] if task_matches.one? && ws_matches.empty?
503
+ return [ws_matches.first, false] if ws_matches.one? && task_matches.empty?
504
+ raise Error, "No task or workspace matches #{first}" if task_matches.empty? && ws_matches.empty?
505
+ names = (task_matches.map(&:name) + ws_matches.map { |w| "#{w.name} (workspace)" }).join(', ')
506
+ raise Error, "Ambiguous #{first}: #{names}" unless $stdin.tty?
507
+ target = pick_switch_target(task_matches, ws_matches)
508
+ return [target, target.is_a?(Hiiro::TaskRecord)]
509
+ end
510
+ args.shift if first == '.'
511
+ no_args!(args)
512
+ begin
513
+ [Hiiro::CurrentTask.new(herdr: -> { herdr_client }, pin: true).resolve!, false]
514
+ rescue Hiiro::Error => e
515
+ raise unless $stdin.tty? && e.message.start_with?('No current task')
516
+ target = pick_switch_target(Hiiro::TaskRecord.all_as_list, loose_workspaces)
517
+ [target, target.is_a?(Hiiro::TaskRecord)]
518
+ end
519
+ end
520
+
453
521
  # --- Worktrees (shared with h task via Hiiro::TaskManager) ---
454
522
 
455
523
  def tree_manager
@@ -668,10 +736,15 @@ class Hiiro
668
736
  end
669
737
 
670
738
  add_option :directory, desc: 'Existing start directory'
671
- add_cmd(:switch, :workspace, args: ['task?'], opts: [*TASK_OPTIONS, :directory, :show]) do
672
- explicit = opts.task || opts.find || (opts.args.first && opts.args.first != '.')
673
- task = take_task_only(opts.args)
674
- opts.show ? show_workspace(task) : open_workspace(task, save: explicit)
739
+ add_cmd(:switch, :workspace, args: ['task-or-workspace?'], opts: [*TASK_OPTIONS, :directory, :show]) do
740
+ target, explicit = switch_target(opts.args)
741
+ if target.is_a?(Hiiro::Herdr::Workspace)
742
+ raise Error, '--show applies to task workspaces' if opts.show
743
+ check_result(client.focus_workspace(target.id))
744
+ puts target
745
+ else
746
+ opts.show ? show_workspace(target) : open_workspace(target, save: explicit)
747
+ end
675
748
  end
676
749
 
677
750
  [%w[omp], %w[codex cdx], %w[claude cld]].each do |names|
data/lib/hiiro/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  class Hiiro
2
- VERSION = "0.1.368"
2
+ VERSION = "0.1.369"
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.368
4
+ version: 0.1.369
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota