hiiro 0.1.15 → 0.1.17

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: 1976453cdeb8c87f0abb85e7fc0061f7809ea40dfd23f5737838c9ef2352128d
4
- data.tar.gz: 98521457732fceb0b2b33d816c5f53ecceac9f250a235e5c16d8d43f780466f7
3
+ metadata.gz: 30801e00dc876dec391ff411a4f20a500ca411e7d2ebf77318790ace5fc603bc
4
+ data.tar.gz: e6bae4d3c68e5edf720f1eeb0b282f9c15ca1c662ff460eb2bae25b38dd30904
5
5
  SHA512:
6
- metadata.gz: 97f152f96b8a4af4f0b956f21dd5a6d9232f5bf3f3aa3093cdaef897914b9379b9e5e754a87cb1a274ab27ecc7bd6cd2a03843b547ffc6a96ea6acaffbfbff35
7
- data.tar.gz: 6668446ba8ec596bd7ad163d7519ab90181c1af5632b335090b79bc438787153b43ebbf419324890c6e9caa6bdedc0e63cbddc59b687ef829f61605d9e79054f
6
+ metadata.gz: 74879d93880ef6c1ed72777ca0fbabe19273d22d2acc7f42ddcbb12a80db439ae0a0a09b7b4970483235b591c69e71e6dbb1015cac3d10364bff2e794fd0a7e9
7
+ data.tar.gz: 35c0a0a4f90e2fb22bd3129131ad8904c12ba3d528a7e6a0d2568656767c97620235c7e4d6180d968a5ab3926fdb104cff5a9230416ef94a79ed76e598276a73
data/bin/h-link CHANGED
@@ -39,9 +39,15 @@ def hash_matches?(links, *args)
39
39
  end
40
40
  end
41
41
 
42
- def link_matches?(link, *args)
42
+ def search_links(links, *args)
43
43
  terms = args.map(&:downcase)
44
44
 
45
+ links.select do |link|
46
+ link_matches?(link, *terms)
47
+ end
48
+ end
49
+
50
+ def link_matches?(link, *terms)
45
51
  searchable = [
46
52
  link['url'],
47
53
  link['description'],
@@ -234,6 +240,8 @@ o.add_subcmd(:open) do |*args|
234
240
  if args.empty?
235
241
  lines = load_link_hash(links)
236
242
 
243
+ exit_code = 1
244
+
237
245
  if args.any?
238
246
  lines = hash_matches?(lines, *args)
239
247
  end
@@ -244,12 +252,23 @@ o.add_subcmd(:open) do |*args|
244
252
  if status.success? && !selected.strip.empty?
245
253
  if selected =~ /^\s*(\d+)\.\s+(\S+)/
246
254
  system('open', $2)
255
+ exit_code = 0
247
256
  end
248
257
  end
258
+
259
+ exit exit_code
249
260
  end
250
261
 
251
262
  idx, link = find_link_by_ref(args.first, links)
252
263
 
264
+ if link.nil?
265
+ matches = search_links(links, *args)
266
+
267
+ if matches.count == 1
268
+ link = matches.first
269
+ end
270
+ end
271
+
253
272
  if link.nil?
254
273
  puts "Link not found: #{args.first}"
255
274
  exit 1
@@ -0,0 +1,268 @@
1
+ require 'yaml'
2
+ require 'time'
3
+ require 'fileutils'
4
+
5
+ class Hiiro
6
+ class History
7
+ HISTORY_FILE = File.join(Dir.home, '.config/hiiro/history.yml')
8
+
9
+ class Entry
10
+ attr_reader :id, :timestamp, :source, :cmd, :description
11
+ attr_reader :tmux_session, :tmux_window, :tmux_pane
12
+ attr_reader :git_branch, :git_worktree
13
+ attr_reader :task, :subtask
14
+
15
+ def initialize(data)
16
+ @id = data['id']
17
+ @timestamp = data['timestamp']
18
+ @source = data['source']
19
+ @cmd = data['cmd']
20
+ @description = data['description']
21
+ @tmux_session = data['tmux_session']
22
+ @tmux_window = data['tmux_window']
23
+ @tmux_pane = data['tmux_pane']
24
+ @git_branch = data['git_branch']
25
+ @git_worktree = data['git_worktree']
26
+ @task = data['task']
27
+ @subtask = data['subtask']
28
+ end
29
+
30
+ def to_h
31
+ {
32
+ 'id' => id,
33
+ 'timestamp' => timestamp,
34
+ 'source' => source,
35
+ 'cmd' => cmd,
36
+ 'description' => description,
37
+ 'tmux_session' => tmux_session,
38
+ 'tmux_window' => tmux_window,
39
+ 'tmux_pane' => tmux_pane,
40
+ 'git_branch' => git_branch,
41
+ 'git_worktree' => git_worktree,
42
+ 'task' => task,
43
+ 'subtask' => subtask,
44
+ }.compact
45
+ end
46
+
47
+ def short_line(index)
48
+ time_str = Time.parse(timestamp).strftime('%Y-%m-%d %H:%M')
49
+ desc = description || cmd || '(no description)'
50
+ desc = desc[0..60] + '...' if desc.length > 63
51
+ format('%3d %s %s', index, time_str, desc)
52
+ end
53
+
54
+ def full_display
55
+ lines = []
56
+ lines << "ID: #{id}"
57
+ lines << "Timestamp: #{timestamp}"
58
+ lines << "Description: #{description}" if description
59
+ lines << "Command: #{cmd}" if cmd
60
+ lines << "Source: #{source}" if source
61
+ lines << ""
62
+ lines << "Tmux:"
63
+ lines << " Session: #{tmux_session}" if tmux_session
64
+ lines << " Window: #{tmux_window}" if tmux_window
65
+ lines << " Pane: #{tmux_pane}" if tmux_pane
66
+ lines << ""
67
+ lines << "Git:"
68
+ lines << " Branch: #{git_branch}" if git_branch
69
+ lines << " Worktree: #{git_worktree}" if git_worktree
70
+ lines << ""
71
+ lines << "Task:"
72
+ lines << " Task: #{task}" if task
73
+ lines << " Subtask: #{subtask}" if subtask
74
+ lines.join("\n")
75
+ end
76
+ end
77
+
78
+ class << self
79
+ def load(hiiro)
80
+ history = new
81
+
82
+ hiiro.add_subcmd(:history) do |*args|
83
+ subcmd, *rest = args
84
+ case subcmd
85
+ when 'ls', 'list', nil
86
+ history.list
87
+ when 'show'
88
+ history.show(rest.first)
89
+ when 'goto'
90
+ history.goto(rest.first)
91
+ when 'add'
92
+ history.add_manual(rest.join(' '), hiiro: hiiro)
93
+ else
94
+ puts "Unknown history subcommand: #{subcmd}"
95
+ puts "Available: ls, show <id>, goto <id>, add <description>"
96
+ false
97
+ end
98
+ end
99
+ end
100
+
101
+ def log(description: nil, pwd: Dir.pwd, cmd: nil, source: nil, task: nil, subtask: nil)
102
+ new.add(
103
+ description: description,
104
+ cmd: cmd,
105
+ pwd: pwd,
106
+ source: source,
107
+ task: task,
108
+ subtask: subtask
109
+ )
110
+ end
111
+ end
112
+
113
+ def initialize
114
+ ensure_history_file
115
+ end
116
+
117
+ def entries
118
+ @entries ||= load_entries
119
+ end
120
+
121
+ def reload
122
+ @entries = nil
123
+ entries
124
+ end
125
+
126
+ def list
127
+ if entries.empty?
128
+ puts "No history entries."
129
+ return true
130
+ end
131
+
132
+ entries.each_with_index do |entry, idx|
133
+ puts entry.short_line(idx + 1)
134
+ end
135
+ true
136
+ end
137
+
138
+ def show(ref)
139
+ entry = find_entry(ref)
140
+ unless entry
141
+ puts "Entry not found: #{ref}"
142
+ return false
143
+ end
144
+
145
+ puts entry.full_display
146
+ true
147
+ end
148
+
149
+ def goto(ref)
150
+ entry = find_entry(ref)
151
+ unless entry
152
+ puts "Entry not found: #{ref}"
153
+ return false
154
+ end
155
+
156
+ unless entry.tmux_session
157
+ puts "No tmux session recorded for this entry"
158
+ return false
159
+ end
160
+
161
+ target = entry.tmux_session
162
+ target += ":#{entry.tmux_window}" if entry.tmux_window
163
+ target += ".#{entry.tmux_pane}" if entry.tmux_pane
164
+
165
+ system('tmux', 'switch-client', '-t', target)
166
+ end
167
+
168
+ def add_manual(description, hiiro: nil)
169
+ add(description: description, source: 'manual')
170
+ puts "Added history entry: #{description}"
171
+ true
172
+ end
173
+
174
+ def add(description: nil, cmd: nil, source: nil, task: nil, subtask: nil, pwd: Dir.pwd)
175
+ cmd ||=
176
+ entry_data = {
177
+ 'id' => generate_id,
178
+ 'timestamp' => Time.now.iso8601,
179
+ 'description' => description,
180
+ 'cmd' => cmd,
181
+ 'pwd' => pwd,
182
+ 'source' => source,
183
+ 'tmux_session' => current_tmux_session,
184
+ 'tmux_window' => current_tmux_window,
185
+ 'tmux_pane' => current_tmux_pane,
186
+ 'git_branch' => current_git_branch,
187
+ 'git_worktree' => current_git_worktree,
188
+ 'task' => task,
189
+ 'subtask' => subtask,
190
+ }.compact
191
+
192
+ all = load_raw_entries
193
+ all << entry_data
194
+ save_entries(all)
195
+
196
+ Entry.new(entry_data)
197
+ end
198
+
199
+ private
200
+
201
+ def find_entry(ref)
202
+ return nil unless ref
203
+
204
+ if ref.to_s.match?(/^\d+$/)
205
+ idx = ref.to_i - 1
206
+ return entries[idx] if idx >= 0 && idx < entries.length
207
+ end
208
+
209
+ entries.find { |e| e.id == ref.to_s }
210
+ end
211
+
212
+ def generate_id
213
+ Time.now.strftime('%Y%m%d%H%M%S') + '-' + rand(10000).to_s.rjust(4, '0')
214
+ end
215
+
216
+ def ensure_history_file
217
+ dir = File.dirname(HISTORY_FILE)
218
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
219
+
220
+ unless File.exist?(HISTORY_FILE)
221
+ File.write(HISTORY_FILE, [].to_yaml)
222
+ end
223
+ end
224
+
225
+ def load_raw_entries
226
+ YAML.load_file(HISTORY_FILE) || []
227
+ rescue => e
228
+ []
229
+ end
230
+
231
+ def load_entries
232
+ load_raw_entries.map { |data| Entry.new(data) }
233
+ end
234
+
235
+ def save_entries(entries_data)
236
+ File.write(HISTORY_FILE, entries_data.to_yaml)
237
+ end
238
+
239
+ def current_tmux_session
240
+ return nil unless ENV['TMUX']
241
+ `tmux display-message -p '#S'`.strip rescue nil
242
+ end
243
+
244
+ def current_tmux_window
245
+ return nil unless ENV['TMUX']
246
+ `tmux display-message -p '#I'`.strip rescue nil
247
+ end
248
+
249
+ def current_tmux_pane
250
+ return nil unless ENV['TMUX']
251
+ `tmux display-message -p '#P'`.strip rescue nil
252
+ end
253
+
254
+ def current_git_branch
255
+ branch = `git branch --show-current 2>/dev/null`.strip
256
+ branch.empty? ? nil : branch
257
+ rescue
258
+ nil
259
+ end
260
+
261
+ def current_git_worktree
262
+ worktree = `git rev-parse --show-toplevel 2>/dev/null`.strip
263
+ worktree.empty? ? nil : worktree
264
+ rescue
265
+ nil
266
+ end
267
+ end
268
+ end
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.15"
2
+ VERSION = "0.1.17"
3
3
  end
data/lib/hiiro.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require "fileutils"
2
2
  require "yaml"
3
+ require "shellwords"
3
4
 
4
5
  require_relative "hiiro/version"
6
+ require_relative "hiiro/history"
5
7
 
6
8
  class Hiiro
7
9
  def self.init(*args, plugins: [], logging: false, **values, &block)
@@ -9,6 +11,7 @@ class Hiiro
9
11
  args = ARGV if args.empty?
10
12
 
11
13
  new($0, *args, logging: logging, **values).tap do |hiiro|
14
+ History.load(hiiro)
12
15
  hiiro.load_plugins(*plugins)
13
16
 
14
17
  hiiro.add_subcmd(:edit, **values) { |*args|
@@ -33,23 +36,32 @@ class Hiiro
33
36
  self
34
37
  end
35
38
 
36
- attr_reader :bin, :bin_name, :all_args
39
+ attr_reader :bin, :bin_name, :all_args, :full_command
37
40
  attr_reader :subcmd, :args
38
41
  attr_reader :loaded_plugins
39
42
  attr_reader :logging
40
43
  attr_reader :global_values
41
44
 
42
- def initialize(bin, *args, logging: false, **values)
45
+ def initialize(bin, *all_args, logging: false, **values)
43
46
  @bin = bin
44
47
  @bin_name = File.basename(bin)
45
- @all_args = args
46
- @subcmd, *@args = args # normally i would never do this
48
+ @all_args = all_args
49
+ @subcmd, *@args = all_args # normally i would never do this
47
50
  @loaded_plugins = []
48
51
  @logging = logging
49
52
  @global_values = values
53
+ @full_command = [
54
+ bin,
55
+ all_args,
56
+ ].map(&:shellescape).join(' ')
57
+ end
58
+
59
+ def history
60
+ @history ||= History.new
50
61
  end
51
62
 
52
63
  def run
64
+ log(description: 'hiiro: ran command')
53
65
  result = runner.run(*args)
54
66
 
55
67
  handle_result(result)
@@ -61,6 +73,15 @@ class Hiiro
61
73
  exit 1
62
74
  end
63
75
 
76
+ def log(**opts)
77
+ history.add(
78
+ description: 'command ran',
79
+ cmd: full_command,
80
+ source: 'hiiro',
81
+ **opts,
82
+ )
83
+ end
84
+
64
85
  def handle_result(result)
65
86
  exit 0 if result.nil? || result
66
87
 
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.15
4
+ version: 0.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota
@@ -61,6 +61,7 @@ files:
61
61
  - exe/h
62
62
  - hiiro.gemspec
63
63
  - lib/hiiro.rb
64
+ - lib/hiiro/history.rb
64
65
  - lib/hiiro/version.rb
65
66
  - links.backup.yml
66
67
  - plugins/notify.rb