hiiro 0.1.366 → 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.
data/exe/t ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'hiiro'
4
+
5
+ Hiiro.run(*ARGV, external_commands: false, builtin_commands: false) do
6
+ Hiiro::TaskCli.setup(self)
7
+ end
data/exe/tt ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'hiiro'
4
+
5
+ Hiiro.run(*ARGV, external_commands: false, builtin_commands: false) do
6
+ Hiiro::TaskCli.setup_todo(self)
7
+ end
@@ -0,0 +1,92 @@
1
+ class Hiiro
2
+ # Resolves the current task record shared by `t` (via TaskScope) and
3
+ # Environment#task (via `h` commands):
4
+ #
5
+ # 1. Herdr workspace, when HERDR_* env vars identify one
6
+ # 2. Working directory inside a task home, code directory, or registered directory
7
+ # 3. Saved `t TASK current` pin (only when pin: true)
8
+ #
9
+ # resolve! raises Hiiro::Error with a user-facing message; resolve returns nil instead.
10
+ class CurrentTask
11
+ def initialize(herdr: nil, cwd: Dir.pwd, pin: true, tasks: nil)
12
+ @herdr = herdr
13
+ @cwd = cwd
14
+ @pin = pin
15
+ @tasks = tasks
16
+ end
17
+
18
+ def resolve
19
+ resolve!
20
+ rescue Hiiro::Error
21
+ nil
22
+ end
23
+
24
+ def resolve!
25
+ herdr_task || directory_task || pinned_task
26
+ end
27
+
28
+ private
29
+
30
+ def tasks
31
+ @tasks ||= TaskRecord.all_as_list
32
+ end
33
+
34
+ def herdr_task
35
+ return nil unless @herdr && (ENV['HERDR_WORKSPACE_ID'] || ENV['HERDR_PANE_ID'] || ENV['HERDR_ENV'] == '1')
36
+
37
+ herdr = @herdr.call
38
+ unless herdr.server_running?
39
+ raise Error, 'Herdr is not running; start Herdr for workspace/tab/pane commands, or supply a task name for task data'
40
+ end
41
+ pane = herdr.get_pane(ENV['HERDR_PANE_ID']) if ENV['HERDR_PANE_ID']
42
+ raise Error, 'Cannot resolve the current Herdr pane; supply a task name' if ENV['HERDR_PANE_ID'] && !pane
43
+
44
+ workspace_id = ENV['HERDR_WORKSPACE_ID'] || pane&.workspace_id
45
+ raise Error, 'Conflicting Herdr pane/workspace context; supply a task name' if pane && workspace_id != pane.workspace_id
46
+
47
+ workspace = workspace_id ? herdr.get_workspace(workspace_id) : herdr.current_workspace
48
+ raise Error, 'Cannot resolve the current Herdr workspace; supply a task name' unless workspace
49
+
50
+ matches = tasks.select { |task| workspace_label(task) == workspace.name }
51
+ raise Error, "Ambiguous workspace task: #{matches.map(&:name).join(', ')}" if matches.length > 1
52
+
53
+ matches.first
54
+ end
55
+
56
+ def directory_task
57
+ cwd = File.realpath(@cwd)
58
+ registered = TaskResource.where(kind: 'directory').select_map([:task_id, :target]).group_by(&:first)
59
+ matches = tasks.select do |task|
60
+ paths = [task.home, code_directory(task), *registered.fetch(task.id, []).map(&:last)].compact
61
+ paths.any? { |path| inside?(cwd, path) }
62
+ end
63
+ raise Error, "Ambiguous directory task: #{matches.map(&:name).join(', ')}" if matches.length > 1
64
+
65
+ matches.first
66
+ end
67
+
68
+ def pinned_task
69
+ raise Error, 'No current task; supply a task name or run t TASK current' unless @pin
70
+
71
+ saved = PinRecord.find_key('t', 'current_task')
72
+ raise Error, 'No current task; supply a task name or run t TASK current' unless saved
73
+
74
+ TaskRecord[saved.value] || raise(Error, 'Saved task no longer exists; run t TASK current')
75
+ end
76
+
77
+ def workspace_label(task)
78
+ (task.session || task.name).tr('.', '_')
79
+ end
80
+
81
+ def code_directory(task)
82
+ task.primary_directory || (task.tree && (task.tree.start_with?('/') ? task.tree : File.join(Hiiro::WORK_DIR, task.tree)))
83
+ end
84
+
85
+ def inside?(path, directory)
86
+ return false unless Dir.exist?(directory)
87
+
88
+ root = File.realpath(directory)
89
+ path == root || path.start_with?(root == File::SEPARATOR ? root : root + File::SEPARATOR)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,5 @@
1
+ class Hiiro
2
+ # Raise for expected user-facing failures (bad arguments, missing records).
3
+ # Hiiro#run prints only the message for these, with no backtrace.
4
+ class Error < StandardError; end
5
+ end
@@ -26,7 +26,7 @@ class Hiiro
26
26
 
27
27
  def self.fetch(repo_path: nil)
28
28
  output = if repo_path
29
- `git -C #{repo_path.shellescape} worktree list --porcelain 2>/dev/null`
29
+ `git -C #{Hiiro::Git.repo_dir(repo_path).shellescape} worktree list --porcelain 2>/dev/null`
30
30
  else
31
31
  `git worktree list --porcelain 2>/dev/null`
32
32
  end
data/lib/hiiro/git.rb CHANGED
@@ -7,6 +7,12 @@ require_relative 'git/pr'
7
7
 
8
8
  class Hiiro
9
9
  class Git
10
+ # Directory to pass to `git -C` for a repo path. REPO_PATH points at
11
+ # ~/work/.git, which is a gitfile in the bare layout, so use its parent.
12
+ def self.repo_dir(repo_path)
13
+ File.file?(repo_path.to_s) ? File.dirname(repo_path) : repo_path
14
+ end
15
+
10
16
  def self.root
11
17
  `git rev-parse --show-toplevel 2>/dev/null`.strip
12
18
  end
@@ -128,7 +134,7 @@ class Hiiro
128
134
 
129
135
  def add_worktree_detached(path, repo_path: nil)
130
136
  if repo_path
131
- run_system('-C', repo_path, 'worktree', 'add', '--detach', path)
137
+ run_system('-C', self.class.repo_dir(repo_path), 'worktree', 'add', '--detach', path)
132
138
  else
133
139
  run_system('worktree', 'add', '--detach', path)
134
140
  end
@@ -136,7 +142,7 @@ class Hiiro
136
142
 
137
143
  def move_worktree(from, to, repo_path: nil)
138
144
  if repo_path
139
- run_system('-C', repo_path, 'worktree', 'move', from, to)
145
+ run_system('-C', self.class.repo_dir(repo_path), 'worktree', 'move', from, to)
140
146
  else
141
147
  run_system('worktree', 'move', from, to)
142
148
  end