hiiro 0.1.365 → 0.1.367
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 +4 -4
- data/CHANGELOG.md +53 -343
- data/CLAUDE.md +1 -0
- data/README.md +114 -17
- data/bin/h-bin +20 -0
- data/bin/h-task +1 -0
- data/bin/t +1 -0
- data/bin/tt +1 -0
- data/docs/h-bin.md +49 -2
- data/docs/h-subtask.md +5 -44
- data/docs/h-task.md +123 -596
- data/docs/h.md +1 -2
- data/docs/t.md +437 -0
- data/docs/why-t.md +306 -0
- data/exe/h +1 -0
- data/exe/t +7 -0
- data/exe/tt +7 -0
- data/lib/hiiro/current_task.rb +92 -0
- data/lib/hiiro/error.rb +5 -0
- data/lib/hiiro/git/worktrees.rb +1 -1
- data/lib/hiiro/git.rb +8 -2
- data/lib/hiiro/task_cli.rb +671 -0
- data/lib/hiiro/task_scope.rb +51 -0
- data/lib/hiiro/task_sessions.rb +51 -0
- data/lib/hiiro/tasks.rb +40 -27
- data/lib/hiiro/version.rb +1 -1
- data/lib/hiiro.rb +23 -20
- metadata +16 -2
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
class Hiiro
|
|
2
|
+
class TaskScope
|
|
3
|
+
class Error < Hiiro::Error; end
|
|
4
|
+
|
|
5
|
+
attr_reader :reference
|
|
6
|
+
|
|
7
|
+
def initialize(reference, herdr:)
|
|
8
|
+
@reference = reference.to_s
|
|
9
|
+
raise Error, 'Task reference cannot be empty' if @reference.empty?
|
|
10
|
+
|
|
11
|
+
@herdr = herdr
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def explicit?
|
|
15
|
+
!%w[. -].include?(reference)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def orphan?
|
|
19
|
+
reference == '-'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def task
|
|
23
|
+
return @task if defined?(@task)
|
|
24
|
+
|
|
25
|
+
@task = if orphan?
|
|
26
|
+
nil
|
|
27
|
+
elsif explicit?
|
|
28
|
+
result = Matcher.new(TaskRecord.all_as_list, :name).by_prefix(reference)
|
|
29
|
+
match = result.resolved
|
|
30
|
+
if !match && result.ambiguous?
|
|
31
|
+
raise Error, "Ambiguous task #{reference}: #{result.matches.map { |item| item.item.name }.join(', ')}"
|
|
32
|
+
end
|
|
33
|
+
match&.item
|
|
34
|
+
else
|
|
35
|
+
current_task
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def task!
|
|
40
|
+
raise Error, "Use t - todo for orphan todos; '-' does not select a task" if orphan?
|
|
41
|
+
|
|
42
|
+
task || raise(Error, "Task not found: #{reference}")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def current_task
|
|
48
|
+
CurrentTask.new(herdr: @herdr, pin: true).resolve!
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'shellwords'
|
|
2
|
+
|
|
3
|
+
class Hiiro
|
|
4
|
+
class TaskSessions
|
|
5
|
+
class Error < Hiiro::Error; end
|
|
6
|
+
|
|
7
|
+
def initialize(client, workspace:, directory:)
|
|
8
|
+
@client = client
|
|
9
|
+
@workspace = workspace
|
|
10
|
+
@directory = directory
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run(tool, argv)
|
|
14
|
+
raise Error, "Unknown AI tool: #{tool}" unless %w[omp codex claude].include?(tool)
|
|
15
|
+
|
|
16
|
+
first = argv.first
|
|
17
|
+
resume = first && !first.empty? && 'resume'.start_with?(first)
|
|
18
|
+
args = resume ? argv.drop(1) : argv
|
|
19
|
+
if resume && args.empty?
|
|
20
|
+
panes = @client.panes(workspace: @workspace).select { |pane| pane.agent == tool }
|
|
21
|
+
if panes.length > 1
|
|
22
|
+
raise Error, "Multiple running #{tool} panes: #{panes.map(&:id).join(', ')}; choose a pane or pass native resume arguments"
|
|
23
|
+
end
|
|
24
|
+
if (pane = panes.first)
|
|
25
|
+
raise Error, "Could not focus pane #{pane.id}" unless @client.focus_pane(pane.id)
|
|
26
|
+
|
|
27
|
+
return pane.id
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
command = [tool]
|
|
32
|
+
command << (tool == 'codex' ? 'resume' : '--resume') if resume
|
|
33
|
+
command.concat(args)
|
|
34
|
+
result = @client.new_tab(
|
|
35
|
+
name: tool,
|
|
36
|
+
workspace: @workspace,
|
|
37
|
+
start_directory: @directory,
|
|
38
|
+
focus: true,
|
|
39
|
+
)
|
|
40
|
+
tab_id = result.is_a?(Hash) && result.dig('tab', 'tab_id')
|
|
41
|
+
raise Error, "Could not create #{tool} tab" unless tab_id.is_a?(String) && !tab_id.empty?
|
|
42
|
+
pane_id = result.dig('root_pane', 'pane_id')
|
|
43
|
+
raise Error, "Created #{tool} tab has no root pane" unless pane_id.is_a?(String) && !pane_id.empty?
|
|
44
|
+
unless @client.run_in_pane(pane_id, Shellwords.join(command))
|
|
45
|
+
raise Error, "Could not start #{tool} in pane #{pane_id}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
tab_id
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/hiiro/tasks.rb
CHANGED
|
@@ -175,6 +175,28 @@ class Hiiro
|
|
|
175
175
|
task_name = scope == :subtask ? "#{current_parent_task.name}/#{name}" : name
|
|
176
176
|
subtree_name = scope == :subtask ? "#{current_parent_task.name}/#{name}" : "#{name}/main"
|
|
177
177
|
|
|
178
|
+
target_path = create_tree(subtree_name, sparse_groups: sparse_groups)
|
|
179
|
+
return unless target_path
|
|
180
|
+
|
|
181
|
+
session_name = task_name
|
|
182
|
+
task = Task.new(name: task_name, tree: subtree_name, session: session_name)
|
|
183
|
+
config.save_task(task)
|
|
184
|
+
|
|
185
|
+
base_dir = target_path
|
|
186
|
+
if app_name
|
|
187
|
+
app = environment.find_app(app_name)
|
|
188
|
+
base_dir = app.resolve(target_path) if app
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
Dir.chdir(base_dir)
|
|
192
|
+
hiiro.start_herdr_workspace(session_name, start_directory: Dir.pwd)
|
|
193
|
+
|
|
194
|
+
puts "Started task '#{task_name}' in worktree '#{subtree_name}'"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Create (or reuse an unassigned) worktree under WORK_DIR and return its path,
|
|
198
|
+
# or nil on failure. Shared by `h task start` and `t TASK tree new`.
|
|
199
|
+
def create_tree(subtree_name, sparse_groups: [])
|
|
178
200
|
target_path = File.join(Hiiro::WORK_DIR, subtree_name)
|
|
179
201
|
|
|
180
202
|
git = Hiiro::Git.new(nil, Hiiro::REPO_PATH)
|
|
@@ -184,33 +206,19 @@ class Hiiro
|
|
|
184
206
|
FileUtils.mkdir_p(File.dirname(target_path))
|
|
185
207
|
unless git.move_worktree(available.path, target_path, repo_path: Hiiro::REPO_PATH)
|
|
186
208
|
puts "ERROR: Failed to rename worktree"
|
|
187
|
-
return
|
|
209
|
+
return nil
|
|
188
210
|
end
|
|
189
211
|
else
|
|
190
212
|
puts "Creating new worktree '#{subtree_name}'..."
|
|
191
213
|
FileUtils.mkdir_p(File.dirname(target_path))
|
|
192
214
|
unless git.add_worktree_detached(target_path, repo_path: Hiiro::REPO_PATH)
|
|
193
215
|
puts "ERROR: Failed to create worktree"
|
|
194
|
-
return
|
|
216
|
+
return nil
|
|
195
217
|
end
|
|
196
218
|
end
|
|
197
219
|
|
|
198
220
|
apply_sparse_checkout(target_path, sparse_groups) if sparse_groups.any?
|
|
199
|
-
|
|
200
|
-
session_name = task_name
|
|
201
|
-
task = Task.new(name: task_name, tree: subtree_name, session: session_name)
|
|
202
|
-
config.save_task(task)
|
|
203
|
-
|
|
204
|
-
base_dir = target_path
|
|
205
|
-
if app_name
|
|
206
|
-
app = environment.find_app(app_name)
|
|
207
|
-
base_dir = app.resolve(target_path) if app
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
Dir.chdir(base_dir)
|
|
211
|
-
hiiro.start_herdr_workspace(session_name, start_directory: Dir.pwd)
|
|
212
|
-
|
|
213
|
-
puts "Started task '#{task_name}' in worktree '#{subtree_name}'"
|
|
221
|
+
target_path
|
|
214
222
|
end
|
|
215
223
|
|
|
216
224
|
def switch_to_task(task, app_name: nil, force: false)
|
|
@@ -1463,10 +1471,11 @@ class Hiiro
|
|
|
1463
1471
|
end
|
|
1464
1472
|
|
|
1465
1473
|
def name
|
|
1466
|
-
@name ||=
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1474
|
+
@name ||= begin
|
|
1475
|
+
roots = [Hiiro::WORK_DIR]
|
|
1476
|
+
roots << File.realpath(Hiiro::WORK_DIR) if Dir.exist?(Hiiro::WORK_DIR)
|
|
1477
|
+
root = roots.uniq.find { |dir| path.start_with?(dir + '/') }
|
|
1478
|
+
root ? path.delete_prefix(root + '/') : File.basename(path)
|
|
1470
1479
|
end
|
|
1471
1480
|
end
|
|
1472
1481
|
|
|
@@ -1683,13 +1692,17 @@ class Hiiro
|
|
|
1683
1692
|
end
|
|
1684
1693
|
|
|
1685
1694
|
def task
|
|
1695
|
+
return @task if defined?(@task)
|
|
1696
|
+
|
|
1697
|
+
s = session
|
|
1698
|
+
t = tree
|
|
1699
|
+
@task = all_tasks.find { |task|
|
|
1700
|
+
(s && task.session_name == s.name) ||
|
|
1701
|
+
(t && (task.tree_name == t.name || task.tree_name == t.path))
|
|
1702
|
+
}
|
|
1686
1703
|
@task ||= begin
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
all_tasks.find { |task|
|
|
1690
|
-
(s && task.session_name == s.name) ||
|
|
1691
|
-
(t && (task.tree_name == t.name || task.tree_name == t.path))
|
|
1692
|
-
}
|
|
1704
|
+
record = CurrentTask.new(herdr: -> { Hiiro::Herdr.client }, cwd: path, pin: false).resolve
|
|
1705
|
+
record && all_tasks.find { |task| task.name == record.name }
|
|
1693
1706
|
end
|
|
1694
1707
|
end
|
|
1695
1708
|
|
data/lib/hiiro/version.rb
CHANGED
data/lib/hiiro.rb
CHANGED
|
@@ -6,6 +6,7 @@ require "ostruct"
|
|
|
6
6
|
require 'awesome_print'
|
|
7
7
|
|
|
8
8
|
require_relative "hiiro/version"
|
|
9
|
+
require_relative "hiiro/error"
|
|
9
10
|
require_relative "hiiro/config"
|
|
10
11
|
require_relative "hiiro/effects"
|
|
11
12
|
require_relative "hiiro/db"
|
|
@@ -46,6 +47,10 @@ require_relative "hiiro/pane_home"
|
|
|
46
47
|
require_relative "hiiro/pin_record"
|
|
47
48
|
require_relative "hiiro/reminder"
|
|
48
49
|
require_relative 'hiiro/registry'
|
|
50
|
+
require_relative 'hiiro/current_task'
|
|
51
|
+
require_relative 'hiiro/task_scope'
|
|
52
|
+
require_relative 'hiiro/task_sessions'
|
|
53
|
+
require_relative 'hiiro/task_cli'
|
|
49
54
|
require_relative 'hiiro/tui'
|
|
50
55
|
|
|
51
56
|
class String
|
|
@@ -65,7 +70,7 @@ class Hiiro
|
|
|
65
70
|
WORK_DIR = File.join(Dir.home, 'work')
|
|
66
71
|
REPO_PATH = File.join(WORK_DIR, '.git')
|
|
67
72
|
|
|
68
|
-
def self.init(*oargs, plugins: [], logging: false, tasks: false, task_scope: nil, **values, &block)
|
|
73
|
+
def self.init(*oargs, plugins: [], logging: false, tasks: false, task_scope: nil, builtin_commands: true, **values, &block)
|
|
69
74
|
load_env
|
|
70
75
|
Hiiro::DB.setup!
|
|
71
76
|
|
|
@@ -98,24 +103,14 @@ class Hiiro
|
|
|
98
103
|
new(h_bin, *h_args, logging: logging, tasks: tasks, task_scope: task_scope, **values).tap do |hiiro|
|
|
99
104
|
hiiro.load_plugins(plugins)
|
|
100
105
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
hiiro.add_subcmd(:edit, **values) { |*args|
|
|
106
|
-
hiiro.edit_files(hiiro.bin)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if hiiro.tasks_enabled?
|
|
110
|
-
hiiro.add_subcmd(:task) do |*args|
|
|
111
|
-
tm = TaskManager.new(hiiro, scope: :task)
|
|
112
|
-
Tasks.build_hiiro(hiiro, tm).run
|
|
113
|
-
end
|
|
106
|
+
if builtin_commands
|
|
107
|
+
hiiro.add_subcommand(:pry) { |*args|
|
|
108
|
+
binding.pry
|
|
109
|
+
}
|
|
114
110
|
|
|
115
|
-
hiiro.add_subcmd(:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
end
|
|
111
|
+
hiiro.add_subcmd(:edit, **values) { |*args|
|
|
112
|
+
hiiro.edit_files(hiiro.bin)
|
|
113
|
+
}
|
|
119
114
|
end
|
|
120
115
|
|
|
121
116
|
if block
|
|
@@ -132,8 +127,8 @@ class Hiiro
|
|
|
132
127
|
Options.setup(&block)
|
|
133
128
|
end
|
|
134
129
|
|
|
135
|
-
def self.run(*args, plugins: [], logging: false, tasks: false, task_scope: nil, **values, &block)
|
|
136
|
-
hiiro = init(*args, plugins:, logging:, tasks:, task_scope:, **values, &block)
|
|
130
|
+
def self.run(*args, plugins: [], logging: false, tasks: false, task_scope: nil, builtin_commands: true, **values, &block)
|
|
131
|
+
hiiro = init(*args, plugins:, logging:, tasks:, task_scope:, builtin_commands:, **values, &block)
|
|
137
132
|
|
|
138
133
|
hiiro.run
|
|
139
134
|
end
|
|
@@ -227,6 +222,12 @@ class Hiiro
|
|
|
227
222
|
Hiiro::Config.open(file, dir: dir)
|
|
228
223
|
end
|
|
229
224
|
|
|
225
|
+
# Open a file, directory, or URL with the OS default handler.
|
|
226
|
+
def open_default(target)
|
|
227
|
+
executable = RUBY_PLATFORM.include?('darwin') ? 'open' : 'xdg-open'
|
|
228
|
+
system(executable, target)
|
|
229
|
+
end
|
|
230
|
+
|
|
230
231
|
def herdr_client
|
|
231
232
|
@herdr_client ||= Herdr.client!(self)
|
|
232
233
|
end
|
|
@@ -276,6 +277,8 @@ class Hiiro
|
|
|
276
277
|
handle_result(result)
|
|
277
278
|
|
|
278
279
|
exit 1
|
|
280
|
+
rescue Hiiro::Error => e
|
|
281
|
+
abort "ERROR: #{e.message}"
|
|
279
282
|
rescue => e
|
|
280
283
|
puts "ERROR: #{e.message}"
|
|
281
284
|
puts e.backtrace
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hiiro
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.367
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joshua Toyota
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: pry
|
|
@@ -129,6 +129,8 @@ email:
|
|
|
129
129
|
executables:
|
|
130
130
|
- h
|
|
131
131
|
- hiiro
|
|
132
|
+
- t
|
|
133
|
+
- tt
|
|
132
134
|
extensions: []
|
|
133
135
|
extra_rdoc_files: []
|
|
134
136
|
files:
|
|
@@ -170,11 +172,14 @@ files:
|
|
|
170
172
|
- bin/h-sha
|
|
171
173
|
- bin/h-sparse
|
|
172
174
|
- bin/h-tags
|
|
175
|
+
- bin/h-task
|
|
173
176
|
- bin/h-time
|
|
174
177
|
- bin/h-title
|
|
175
178
|
- bin/h-todo
|
|
176
179
|
- bin/h-window
|
|
177
180
|
- bin/h-wtree
|
|
181
|
+
- bin/t
|
|
182
|
+
- bin/tt
|
|
178
183
|
- bkup..ruby-version
|
|
179
184
|
- demo.sh
|
|
180
185
|
- demo.typescript
|
|
@@ -325,6 +330,8 @@ files:
|
|
|
325
330
|
- docs/h-window.md
|
|
326
331
|
- docs/h-wtree.md
|
|
327
332
|
- docs/h.md
|
|
333
|
+
- docs/t.md
|
|
334
|
+
- docs/why-t.md
|
|
328
335
|
- editboth
|
|
329
336
|
- examples/bin-with-proposed-options
|
|
330
337
|
- examples/services.sleepers.yml
|
|
@@ -332,6 +339,8 @@ files:
|
|
|
332
339
|
- examples/sparse_groups.yml
|
|
333
340
|
- exe/h
|
|
334
341
|
- exe/hiiro
|
|
342
|
+
- exe/t
|
|
343
|
+
- exe/tt
|
|
335
344
|
- h-tmux-plugins.tmux
|
|
336
345
|
- hiiro.gemspec
|
|
337
346
|
- lib/hiiro.rb
|
|
@@ -345,8 +354,10 @@ files:
|
|
|
345
354
|
- lib/hiiro/capture.rb
|
|
346
355
|
- lib/hiiro/check_run.rb
|
|
347
356
|
- lib/hiiro/config.rb
|
|
357
|
+
- lib/hiiro/current_task.rb
|
|
348
358
|
- lib/hiiro/db.rb
|
|
349
359
|
- lib/hiiro/effects.rb
|
|
360
|
+
- lib/hiiro/error.rb
|
|
350
361
|
- lib/hiiro/fuzzyfind.rb
|
|
351
362
|
- lib/hiiro/git.rb
|
|
352
363
|
- lib/hiiro/git/branch.rb
|
|
@@ -379,7 +390,10 @@ files:
|
|
|
379
390
|
- lib/hiiro/service_manager.rb
|
|
380
391
|
- lib/hiiro/shell.rb
|
|
381
392
|
- lib/hiiro/tags.rb
|
|
393
|
+
- lib/hiiro/task_cli.rb
|
|
382
394
|
- lib/hiiro/task_record.rb
|
|
395
|
+
- lib/hiiro/task_scope.rb
|
|
396
|
+
- lib/hiiro/task_sessions.rb
|
|
383
397
|
- lib/hiiro/tasks.rb
|
|
384
398
|
- lib/hiiro/todo.rb
|
|
385
399
|
- lib/hiiro/tui.rb
|