hiiro 0.1.105 → 0.1.107

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: ed2ec948d47eefe0c380d4d356ff55d7b2542bd40d47b797d93ce75301a2f843
4
- data.tar.gz: 72df03eb994937c6b77284075711b7c6b8554a71da9f5527013edda8bd2bd55b
3
+ metadata.gz: '00583cf6d27d5bf15012a0fcb366f5f89b57b68e02963de6ea1462d696f95712'
4
+ data.tar.gz: 6283dabff0534fc659f4a7fb23019881c5ce55b25697da23b445b02caa562cbc
5
5
  SHA512:
6
- metadata.gz: 07cc2ba096b090a981b7a01c23813aeb46b3f44f5b8952e1582f05aa89d57a29382085f4fc81ec91d2171a12d7e140fd0014cb1e63c1baafb7a707b31cd4dadc
7
- data.tar.gz: f778710abfc26fe7d6d89ad688dabe99efae892cad21daa6184d456d48cddb605ffc11c9eadcd9b06758fead837bd9d84cf827555cddbac94e7875a97614edf8
6
+ metadata.gz: 3f9ee12a59e91c458186e14a040e19700e282251ec33fe585a8aea21198bb7c38da0a2fcd2a2c561d74ed0a684644c1b6436e8fb159f9f9008a140dac6f5521a
7
+ data.tar.gz: f17af6e37572b53a322b660db8d59e400456d60461212da54d72863327ee02ff20ef107ec169c44955ac814e61ce51078d7257940e6c232972ae595da80aab8f
data/lib/hiiro/queue.rb CHANGED
@@ -50,6 +50,22 @@ class Hiiro
50
50
  File.exist?(path) ? YAML.safe_load_file(path) : nil
51
51
  end
52
52
 
53
+ def task_preview(name, status)
54
+ path = File.join(queue_dirs[status], "#{name}.md")
55
+ return nil unless File.exist?(path)
56
+
57
+ lines = File.readlines(path, chomp: true)
58
+ # Skip frontmatter
59
+ if lines.first == '---'
60
+ end_idx = lines[1..].index('---')
61
+ lines = lines[(end_idx + 2)..] if end_idx
62
+ end
63
+ first = lines&.find { |l| !l.strip.empty? }&.strip
64
+ return nil unless first
65
+
66
+ first.length > 60 ? "| #{first[0, 57]}..." : "| #{first}"
67
+ end
68
+
53
69
  def find_task(name)
54
70
  STATUSES.each do |status|
55
71
  md = File.join(queue_dirs[status.to_sym], "#{name}.md")
@@ -73,7 +89,6 @@ class Hiiro
73
89
  FileUtils.mv(md_file, running_md)
74
90
 
75
91
  prompt_obj = Prompt.from_file(running_md, hiiro: hiiro)
76
- prompt_text = File.read(running_md).strip
77
92
 
78
93
  # Determine target tmux session and working directory from frontmatter
79
94
  target_session = TMUX_SESSION
@@ -98,24 +113,44 @@ class Hiiro
98
113
  system('tmux', 'new-session', '-d', '-s', target_session, '-c', working_dir)
99
114
  end
100
115
 
101
- # Build the cleanup script that runs after claude exits
102
- cleanup_ruby = [
103
- 'ruby', '-e',
104
- 'require "fileutils"; ' \
105
- "name=#{name.inspect}; qdir=#{DIR.inspect}; " \
106
- 'exit_code = ENV["HQ_EXIT"].to_i; ' \
107
- 'src=File.join(qdir,"running",name+".md"); ' \
108
- 'dst_dir = exit_code == 0 ? "done" : "failed"; ' \
109
- 'FileUtils.mv(src, File.join(qdir, dst_dir, name+".md")) if File.exist?(src); ' \
110
- 'meta=File.join(qdir,"running",name+".meta"); ' \
111
- 'FileUtils.mv(meta, File.join(qdir, dst_dir, name+".meta")) if File.exist?(meta)'
112
- ].shelljoin
113
-
114
- # Run claude interactively in tmux window, then cleanup on exit
115
- shell_cmd = "cd #{Shellwords.shellescape(working_dir)} && claude #{Shellwords.shellescape(prompt_text)}; HQ_EXIT=$?; #{cleanup_ruby}; exec #{ENV['SHELL'] || 'zsh'}"
116
+ # Write a clean prompt file (no frontmatter) for claude
117
+ raw = File.read(running_md).strip
118
+ prompt_body = prompt_obj ? prompt_obj.doc.content.strip : strip_frontmatter(raw)
119
+ prompt_file = File.join(dirs[:running], "#{name}.prompt")
120
+ File.write(prompt_file, prompt_body + "\n")
121
+
122
+ # Write a launcher script
123
+ script_path = File.join(dirs[:running], "#{name}.sh")
124
+ File.write(script_path, <<~SH)
125
+ #!/usr/bin/env bash
126
+
127
+ cd #{Shellwords.shellescape(working_dir)}
128
+ cat #{Shellwords.shellescape(prompt_file)} | claude -p
129
+ HQ_EXIT=$?
130
+
131
+ # Move task files to done/failed based on exit code
132
+ ruby -e '
133
+ require "fileutils"
134
+ name = #{name.inspect}
135
+ qdir = #{DIR.inspect}
136
+ exit_code = ENV["HQ_EXIT"].to_i
137
+ dst_dir = exit_code == 0 ? "done" : "failed"
138
+ %w[.md .meta .prompt .sh].each do |ext|
139
+ src = File.join(qdir, "running", name + ext)
140
+ if ext == ".prompt" || ext == ".sh"
141
+ FileUtils.rm_f(src)
142
+ elsif File.exist?(src)
143
+ FileUtils.mv(src, File.join(qdir, dst_dir, name + ext))
144
+ end
145
+ end
146
+ '
147
+
148
+ exec #{Shellwords.shellescape(ENV['SHELL'] || 'zsh')}
149
+ SH
150
+ FileUtils.chmod(0755, script_path)
116
151
 
117
152
  win_name = short_window_name(name)
118
- system('tmux', 'new-window', '-d', '-t', target_session, '-n', win_name, '-c', working_dir, shell_cmd)
153
+ system('tmux', 'new-window', '-d', '-t', target_session, '-n', win_name, '-c', working_dir, script_path)
119
154
 
120
155
  # Write meta sidecar
121
156
  meta = {
@@ -134,6 +169,14 @@ class Hiiro
134
169
  puts "Launched: #{name} [#{target_session}:#{win_name}]"
135
170
  end
136
171
 
172
+ def strip_frontmatter(text)
173
+ lines = text.lines
174
+ return text unless lines.first&.strip == '---'
175
+ end_idx = lines[1..].index { |l| l.strip == '---' }
176
+ return text unless end_idx
177
+ lines[(end_idx + 2)..].join.strip
178
+ end
179
+
137
180
  def short_window_name(name)
138
181
  base = name[0, 8]
139
182
  return base unless existing_window_name?(base)
@@ -228,7 +271,20 @@ class Hiiro
228
271
  next
229
272
  end
230
273
  tasks.each do |t|
231
- puts "%-10s %s" % [t[:status], t[:name]]
274
+ line = "%-10s %s" % [t[:status], t[:name]]
275
+ meta = q.meta_for(t[:name], t[:status].to_sym)
276
+ if meta && t[:status] == 'running'
277
+ started = meta['started_at']
278
+ if started
279
+ elapsed = Time.now - Time.parse(started)
280
+ mins = (elapsed / 60).to_i
281
+ line += " (#{mins}m)"
282
+ end
283
+ line += " [#{meta['tmux_session']}:#{meta['tmux_window']}]" if meta['tmux_session']
284
+ end
285
+ preview = q.task_preview(t[:name], t[:status].to_sym)
286
+ line += " #{preview}" if preview
287
+ puts line
232
288
  end
233
289
  }
234
290
 
@@ -239,7 +295,20 @@ class Hiiro
239
295
  next
240
296
  end
241
297
  tasks.each do |t|
242
- puts "%-10s %s" % [t[:status], t[:name]]
298
+ line = "%-10s %s" % [t[:status], t[:name]]
299
+ meta = q.meta_for(t[:name], t[:status].to_sym)
300
+ if meta && t[:status] == 'running'
301
+ started = meta['started_at']
302
+ if started
303
+ elapsed = Time.now - Time.parse(started)
304
+ mins = (elapsed / 60).to_i
305
+ line += " (#{mins}m)"
306
+ end
307
+ line += " [#{meta['tmux_session']}:#{meta['tmux_window']}]" if meta['tmux_session']
308
+ end
309
+ preview = q.task_preview(t[:name], t[:status].to_sym)
310
+ line += " #{preview}" if preview
311
+ puts line
243
312
  end
244
313
  }
245
314
 
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.105"
2
+ VERSION = "0.1.107"
3
3
  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.105
4
+ version: 0.1.107
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota