sight 0.3.0 → 0.5.0

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: 6e488680c7d4ecc42d8cd02b8d2731a315755b35df2a9cf319ed3f0d413e2809
4
- data.tar.gz: 375af2bad773014e984f0b89c3333c839dd84bfc8e8cd610357c78bcfd052962
3
+ metadata.gz: 32b20a4a62fbca9fee67a4354538a8e033dc079b945c7aa5a6f6d23f63c0fac6
4
+ data.tar.gz: 4be26d309ed3cb0649bc093a78326d6f9a4a2ce2f2f3f3ec9943b713bac9f372
5
5
  SHA512:
6
- metadata.gz: 76d83afcd3ac25b084fc22661d9561fd75bd16ef662237b5cf2e1d784225a442ac682740a312a711ae169e16eac1f8d997ff3e68b44fb1222a00a9fd3e077c6a
7
- data.tar.gz: 934ab59d60fb58ac4029718705d1551fd6ede47ff0dfcadfefdf45fdd30eddc2060dcc815988060a796cf55cf993e0b9ba82b86426b1bc8043ed974631006099
6
+ metadata.gz: 7acf8bc51543269963f35953c5e7ffe7452483e655330bd5d9c314c394a575f6710233fe26b117eaa7ca00491c0d4f561c879e2c7ab160fcb32a6d3db15bf7e4
7
+ data.tar.gz: 696a3493aa44d9e7913913df11f9ef583de057e15bc210811027121a629534d486c7cb7c197180c146644aadba2eea331db8d49315c83da9197d2a4116f472ca
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0] - 2026-03-07
4
+
5
+ ### Removed
6
+
7
+ - Cursor hook support (use Cursor rules instead)
8
+
9
+ ### Changed
10
+
11
+ - Refactored CLI dispatch to use case statement with extracted `open` and `print_help` methods
12
+ - DRYed up install/uninstall hook agent lookup
13
+ - Use `Set` for commented hunk lines lookup
14
+ - Extracted `hunk_end_offset` to deduplicate boundary logic
15
+ - Use `<<` for string building in `AnnotationFormatter`
16
+ - Simplified prompt comment width clamping
17
+
18
+ ## [0.4.0] - 2026-03-06
19
+
20
+ ### Added
21
+
22
+ - Untracked file support with file status badges (new, modified, deleted)
23
+ - Color-coded status badges in file header
24
+ - Ctrl-f/b/d/u scroll navigation
25
+ - Scroll percentage indicator replacing line counter
26
+ - Hook system for AI agent integration (`sight install-hook <agent>`)
27
+ - Claude Code hook via `UserPromptSubmit`
28
+ - Cursor hook via `beforeSubmitPrompt`
29
+ - Highlighted commented hunks in gutter and status bar
30
+
3
31
  ## [0.3.0] - 2026-03-04
4
32
 
5
33
  ### Added
data/CLAUDE.md CHANGED
@@ -13,14 +13,21 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
13
13
 
14
14
  ## Architecture
15
15
 
16
- TUI for closing the loop on AI-generated code changes — browse diffs, jump between hunks, and annotate them. Entry point: `exe/sight` → `CLI.run` → `App.new(files).run`.
16
+ TUI for closing the loop on AI-generated code changes — browse diffs, jump between hunks, and annotate them. Entry point: `exe/sight` → `CLI.run` → `CLI.open` → `App.new(files).run`.
17
17
 
18
- **Data flow**: `Git.diff` (raw string) → `DiffParser.parse` (returns `DiffFile[]`) → `App` (curses TUI)
18
+ **Data flow**: `Git.diff` (raw string) → `DiffParser.parse` (returns `DiffFile[]`) → `App` (curses TUI).
19
+ Untracked files are added via `Git.untracked_files` → `DiffParser.build_untracked`.
19
20
 
20
- **Key structs** (all in `diff_parser.rb`): `DiffFile(path, hunks)`, `Hunk(context, lines)`, `DiffLine(type, content, lineno, old_lineno)`. `DisplayLine(type, text, lineno)` is the render-side equivalent in `display_line.rb`.
21
+ **Key structs** (all in `diff_parser.rb`): `DiffFile(path, hunks, status)`, `Hunk(context, lines)`, `DiffLine(type, content, lineno, old_lineno)`.
22
+ `DisplayLine(type, text, lineno)` is the render-side equivalent in `display_line.rb`.
23
+ `Annotation(file_path, type, hunk, comment)` in `annotation.rb` stores per-hunk comments; `AnnotationFormatter` serializes them for output.
21
24
 
22
25
  **App** renders per-file views with hunk-based navigation (j/k). Active hunk is highlighted; inactive hunks render in dark gray (color pair 5, color 240).
23
26
 
27
+ **Hook system**: `sight install-hook claude` / `sight uninstall-hook claude`.
28
+ `ClaudeHookInstaller` manages a Claude Code `UserPromptSubmit` hook in `~/.config/claude/settings.json`.
29
+ Reads `.git/sight/pending-review`, outputs annotations, and deletes the file. Hidden subcommand: `hook-run`.
30
+
24
31
  ## Conventions
25
32
 
26
33
  - Ruby >= 3.2, uses `frozen_string_literal` in all files
data/README.md CHANGED
@@ -24,10 +24,43 @@ sight
24
24
  | `k` | Previous hunk |
25
25
  | `n` | Next file |
26
26
  | `p` | Previous file |
27
+ | `Ctrl-F` / `Ctrl-B` | Scroll full page down / up |
28
+ | `Ctrl-D` / `Ctrl-U` | Scroll half page down / up |
27
29
  | `?` | Toggle help |
28
30
  | `c` | Comment on hunk |
29
31
  | `q` / `Esc` | Quit |
30
32
 
33
+ ### Agent Integration
34
+
35
+ Install a hook so annotations are automatically fed as context in your next message:
36
+
37
+ **Claude Code** (~/.config/claude/settings.json):
38
+
39
+ ```bash
40
+ sight install-hook claude
41
+ ```
42
+
43
+ When you quit sight after annotating, the next message you send will include your annotations.
44
+
45
+ The hook runs on every prompt but is a no-op when there's no pending review — it produces no output and adds nothing to the agent's context.
46
+
47
+ To remove:
48
+
49
+ ```bash
50
+ sight uninstall-hook claude
51
+ ```
52
+
53
+ **Cursor** — Create a rule in `.cursor/rules/sight.mdc`:
54
+
55
+ ```markdown
56
+ ---
57
+ description: When the user asks to review their sight annotations or code review comments
58
+ alwaysApply: false
59
+ ---
60
+
61
+ Read `.git/sight/pending-review`, address the annotations, then delete the file.
62
+ ```
63
+
31
64
  ## Development
32
65
 
33
66
  ```bash
data/RELEASING.md ADDED
@@ -0,0 +1,7 @@
1
+ # Releasing
2
+
3
+ 1. Update `lib/sight/version.rb` and `CHANGELOG.md`
4
+ 2. Run `bundle install` to update `Gemfile.lock`
5
+ 3. Run `bundle exec rake` to verify tests and lint pass
6
+ 4. Commit: `git commit -am "Release vX.Y.Z"`
7
+ 5. Run `bundle exec rake release` — tags, builds, and pushes to RubyGems
@@ -8,21 +8,21 @@ module Sight
8
8
  return "" if annotations.empty?
9
9
 
10
10
  grouped = annotations.group_by(&:file_path)
11
- grouped.map { |path, anns| format_file(path, anns) }.join("\n")
11
+ grouped.map { |path, file_annotations| format_file(path, file_annotations) }.join("\n")
12
12
  end
13
13
 
14
14
  def format_file(path, annotations)
15
15
  out = "## File: #{path}\n\n"
16
- annotations.each do |ann|
17
- context = ann.hunk.context ? " #{ann.hunk.context}" : ""
18
- out += "Hunk (@@#{context}):\n"
19
- out += "```diff\n"
20
- ann.hunk.lines.each do |line|
16
+ annotations.each do |annotation|
17
+ context = annotation.hunk.context ? " #{annotation.hunk.context}" : ""
18
+ out << "Hunk (@@#{context}):\n"
19
+ out << "```diff\n"
20
+ annotation.hunk.lines.each do |line|
21
21
  next unless %i[add del].include?(line.type)
22
- out += "#{line.content}\n"
22
+ out << "#{line.content}\n"
23
23
  end
24
- out += "```\n"
25
- out += "> #{ann.comment}\n\n"
24
+ out << "```\n"
25
+ out << "> #{annotation.comment}\n\n"
26
26
  end
27
27
  out
28
28
  end
data/lib/sight/app.rb CHANGED
@@ -53,6 +53,7 @@ module Sight
53
53
  Curses.init_pair(3, Curses::COLOR_CYAN, -1)
54
54
  Curses.init_pair(4, Curses::COLOR_YELLOW, -1)
55
55
  Curses.init_pair(5, 240, -1)
56
+ Curses.init_pair(6, Curses::COLOR_MAGENTA, -1)
56
57
  end
57
58
 
58
59
  def lines
@@ -70,11 +71,19 @@ module Sight
70
71
  end
71
72
 
72
73
  def render_header(win, width)
73
- path = files[file_idx].path
74
+ file = files[file_idx]
75
+ badge = "[#{file.status || :modified}]"
76
+ path = file.path
77
+ gap = width - path.length - badge.length
74
78
  win.setpos(0, 0)
75
- win.attron(color_for(:header)) { win.addstr(path[0, width]) }
79
+ if gap >= 1
80
+ win.attron(color_for(:header)) { win.addstr("#{path}#{" " * gap}") }
81
+ win.attron(badge_color(file.status)) { win.addstr(badge) }
82
+ else
83
+ win.attron(color_for(:header)) { win.addstr(path[0, width]) }
84
+ end
76
85
  win.setpos(1, 0)
77
- win.attron(color_for(:header)) { win.addstr("\u2500" * width) }
86
+ win.attron(Curses.color_pair(0) | Curses::A_BOLD) { win.addstr("\u2500" * width) }
78
87
  end
79
88
 
80
89
  def render_content(win, width)
@@ -83,11 +92,9 @@ module Sight
83
92
  content_width = width - gutter - 3
84
93
 
85
94
  selected_start = hunk_offsets[hunk_idx]
86
- selected_end = if hunk_idx + 1 < hunk_offsets.size
87
- hunk_offsets[hunk_idx + 1]
88
- else
89
- lines.size
90
- end
95
+ selected_end = hunk_end_offset(hunk_idx)
96
+
97
+ commented_lines = commented_hunk_lines
91
98
 
92
99
  scroll_height.times do |row|
93
100
  idx = offset + row
@@ -95,7 +102,13 @@ module Sight
95
102
  line = lines[idx]
96
103
  win.setpos(row + 2, 0)
97
104
  active = idx >= selected_start && idx < selected_end
98
- win.attron(dim) { win.addstr("#{format_gutter(line.type, line.lineno, gutter)} \u2502 ") }
105
+ gutter_str = format_gutter(line.type, line.lineno, gutter)
106
+ commented = commented_lines.include?(idx)
107
+ separator = commented ? "\u2503" : "\u2502"
108
+ sep_attr = commented ? Curses.color_pair(4) : dim
109
+ win.attron(dim) { win.addstr("#{gutter_str} ") }
110
+ win.attron(sep_attr) { win.addstr(separator) }
111
+ win.attron(dim) { win.addstr(" ") }
99
112
  attr = active ? color_for(line.type) : Curses.color_pair(5)
100
113
  win.attron(attr) { win.addstr(line.text[0, content_width]) }
101
114
  end
@@ -104,7 +117,13 @@ module Sight
104
117
  def render_status_bar(win, width)
105
118
  win.setpos(Curses.lines - 1, 0)
106
119
  win.attron(Curses.color_pair(4) | Curses::A_REVERSE) do
107
- status = " File #{file_idx + 1}/#{files.size} | Hunk #{hunk_idx + 1}/#{hunk_offsets.size} | Line #{offset + 1}/#{lines.size} "
120
+ percent = if lines.size <= scroll_height
121
+ 100
122
+ else
123
+ ((offset + scroll_height) * 100.0 / lines.size).ceil.clamp(0, 100)
124
+ end
125
+ commented = hunk_commented?(file_idx, hunk_idx) ? " [commented]" : ""
126
+ status = " File #{file_idx + 1}/#{files.size} | Hunk #{hunk_idx + 1}/#{hunk_offsets.size}#{commented} | #{percent}% "
108
127
  win.addstr(status.ljust(width))
109
128
  end
110
129
  end
@@ -123,11 +142,20 @@ module Sight
123
142
  case type
124
143
  when :add then Curses.color_pair(1)
125
144
  when :del then Curses.color_pair(2)
126
- when :header then Curses.color_pair(4) | Curses::A_BOLD
145
+ when :header then Curses.color_pair(0) | Curses::A_BOLD
127
146
  else Curses.color_pair(0)
128
147
  end
129
148
  end
130
149
 
150
+ def badge_color(status)
151
+ case status
152
+ when :added then Curses.color_pair(1) | Curses::A_BOLD
153
+ when :deleted then Curses.color_pair(2) | Curses::A_BOLD
154
+ when :untracked then Curses.color_pair(6) | Curses::A_BOLD
155
+ else Curses.color_pair(4) | Curses::A_BOLD
156
+ end
157
+ end
158
+
131
159
  def scroll_height
132
160
  Curses.lines - 3
133
161
  end
@@ -147,6 +175,10 @@ module Sight
147
175
  when "k" then jump_hunk(-1)
148
176
  when "n" then jump_file(1)
149
177
  when "p" then jump_file(-1)
178
+ when 6 then scroll(scroll_height)
179
+ when 2 then scroll(-scroll_height)
180
+ when 4 then scroll(scroll_height / 2)
181
+ when 21 then scroll(-scroll_height / 2)
150
182
  when "c" then annotate_hunk
151
183
  when "?" then show_help
152
184
  end
@@ -156,6 +188,10 @@ module Sight
156
188
  HELP_KEYS = [
157
189
  ["j", "Next hunk"],
158
190
  ["k", "Previous hunk"],
191
+ ["C-f", "Page down"],
192
+ ["C-b", "Page up"],
193
+ ["C-d", "Half page down"],
194
+ ["C-u", "Half page up"],
159
195
  ["n", "Next file"],
160
196
  ["p", "Previous file"],
161
197
  ["q / Esc", "Quit"],
@@ -214,9 +250,7 @@ module Sight
214
250
 
215
251
  def prompt_comment(title)
216
252
  win = Curses.stdscr
217
- max_width = 80
218
- width = [Curses.cols * 2 / 3, 50].max
219
- width = [width, max_width].min
253
+ width = (Curses.cols * 2 / 3).clamp(50, 80)
220
254
  height = 5
221
255
  top = (Curses.lines - height) / 2
222
256
  left = (Curses.cols - width) / 2
@@ -271,6 +305,28 @@ module Sight
271
305
  end
272
306
  end
273
307
 
308
+ def hunk_end_offset(idx)
309
+ (idx + 1 < hunk_offsets.size) ? hunk_offsets[idx + 1] : lines.size
310
+ end
311
+
312
+ def hunk_commented?(file_index, hunk_index)
313
+ path = files[file_index].path
314
+ hunk = files[file_index].hunks[hunk_index]
315
+ annotations.any? { |a| a.file_path == path && a.hunk.equal?(hunk) }
316
+ end
317
+
318
+ def commented_hunk_lines
319
+ hunk_offsets.each_with_index.each_with_object(Set.new) do |(offset, hunk_index), set|
320
+ next unless hunk_commented?(file_idx, hunk_index)
321
+ (offset...hunk_end_offset(hunk_index)).each { |i| set << i }
322
+ end
323
+ end
324
+
325
+ def scroll(delta)
326
+ max = [0, lines.size - scroll_height].max
327
+ self.offset = (offset + delta).clamp(0, max)
328
+ end
329
+
274
330
  def jump_hunk(delta)
275
331
  return if hunk_offsets.empty?
276
332
  self.hunk_idx = (hunk_idx + delta).clamp(0, hunk_offsets.size - 1)
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "fileutils"
5
+
6
+ module Sight
7
+ module ClaudeHookInstaller
8
+ HOOK_COMMAND = "sight hook-run"
9
+
10
+ module_function
11
+
12
+ def settings_path
13
+ File.join(Dir.home, ".config", "claude", "settings.json")
14
+ end
15
+
16
+ def install(path: settings_path)
17
+ settings = if File.exist?(path)
18
+ JSON.parse(File.read(path))
19
+ else
20
+ {}
21
+ end
22
+
23
+ hooks = settings["hooks"] ||= {}
24
+ prompt_hooks = hooks["UserPromptSubmit"] ||= []
25
+
26
+ if prompt_hooks.any? { |h| hook_is_sight?(h) }
27
+ puts "sight hook already installed"
28
+ return
29
+ end
30
+
31
+ prompt_hooks << {
32
+ "matcher" => "*",
33
+ "hooks" => [{"type" => "command", "command" => HOOK_COMMAND}]
34
+ }
35
+
36
+ FileUtils.mkdir_p(File.dirname(path))
37
+ File.write(path, JSON.pretty_generate(settings) + "\n")
38
+ puts "Installed sight hook into #{path}"
39
+ end
40
+
41
+ def uninstall(path: settings_path)
42
+ unless File.exist?(path)
43
+ puts "No settings file found"
44
+ return
45
+ end
46
+
47
+ settings = JSON.parse(File.read(path))
48
+ prompt_hooks = settings.dig("hooks", "UserPromptSubmit")
49
+
50
+ unless prompt_hooks&.any? { |h| hook_is_sight?(h) }
51
+ puts "No sight hook found"
52
+ return
53
+ end
54
+
55
+ prompt_hooks.reject! { |h| hook_is_sight?(h) }
56
+ File.write(path, JSON.pretty_generate(settings) + "\n")
57
+ puts "Uninstalled sight hook"
58
+ end
59
+
60
+ def hook_is_sight?(entry)
61
+ Array(entry["hooks"]).any? { |h| h["command"]&.include?("sight") }
62
+ end
63
+ end
64
+ end
data/lib/sight/cli.rb CHANGED
@@ -5,34 +5,84 @@ module Sight
5
5
  module_function
6
6
 
7
7
  def run(argv)
8
- if argv.include?("--help") || argv.include?("-h")
9
- puts "Usage: sight"
10
- puts "Interactive git diff viewer (staged + unstaged)"
11
- puts
12
- puts "Keys: j/k hunks, n/p files, c comment, ? help, q quit"
13
- puts
14
- puts "Annotations are printed to stdout on quit."
15
- return
16
- end
8
+ return print_help if argv.include?("--help") || argv.include?("-h")
9
+ return puts("sight #{VERSION}") if argv.include?("--version") || argv.include?("-v")
17
10
 
18
- if argv.include?("--version") || argv.include?("-v")
19
- puts "sight #{VERSION}"
20
- return
11
+ case argv[0]
12
+ when "install-hook" then install_hook(argv[1])
13
+ when "uninstall-hook" then uninstall_hook(argv[1])
14
+ when "hook-run" then run_hook
15
+ else open
21
16
  end
17
+ end
18
+
19
+ def print_help
20
+ puts "Usage: sight"
21
+ puts "Interactive git diff viewer (staged + unstaged)"
22
+ puts
23
+ puts "Keys: j/k hunks, n/p files, c comment, ? help, q quit"
24
+ puts
25
+ puts "Subcommands:"
26
+ puts " install-hook <agent> Install hook (claude)"
27
+ puts " uninstall-hook <agent> Remove hook (claude)"
28
+ end
29
+
30
+ def open
31
+ Git.clear_pending_review
22
32
 
23
33
  raw = Git.diff
24
- if raw.empty?
25
- warn "No diff output"
34
+ files = DiffParser.parse(raw)
35
+
36
+ Git.untracked_files.each do |path|
37
+ content = Git.file_content(path)
38
+ next unless content.valid_encoding? && !content.include?("\x00")
39
+ files << DiffParser.build_untracked(path, content)
40
+ end
41
+
42
+ if files.empty?
43
+ warn "No changes"
26
44
  return
27
45
  end
28
46
 
29
- files = DiffParser.parse(raw)
30
47
  app = App.new(files)
31
48
  app.run
32
49
 
33
50
  unless app.annotations.empty?
34
- puts AnnotationFormatter.format(app.annotations)
51
+ formatted = AnnotationFormatter.format(app.annotations)
52
+ puts formatted
53
+ Git.save_pending_review(formatted)
35
54
  end
36
55
  end
56
+
57
+ AGENTS = {
58
+ "claude" => ClaudeHookInstaller
59
+ }.freeze
60
+
61
+ def install_hook(agent)
62
+ resolve_installer(agent)&.install
63
+ end
64
+
65
+ def uninstall_hook(agent)
66
+ resolve_installer(agent)&.uninstall
67
+ end
68
+
69
+ def resolve_installer(agent)
70
+ AGENTS.fetch(agent) { warn "Unknown agent: #{agent.inspect}. Use: claude" }
71
+ end
72
+
73
+ def run_hook
74
+ git_dir = Git.repo_dir
75
+ rescue Error
76
+ nil
77
+ else
78
+ file = File.join(git_dir, "sight", "pending-review")
79
+ return unless File.exist?(file)
80
+
81
+ content = File.read(file)
82
+ puts "The user has just finished reviewing your code changes in sight. Here are their annotations:"
83
+ puts
84
+ puts content
85
+ File.delete(file)
86
+ end
37
87
  end
38
88
  end
@@ -3,7 +3,7 @@
3
3
  module Sight
4
4
  DiffLine = Struct.new(:type, :content, :lineno, :old_lineno, keyword_init: true)
5
5
  Hunk = Struct.new(:context, :lines, keyword_init: true)
6
- DiffFile = Struct.new(:path, :hunks, keyword_init: true)
6
+ DiffFile = Struct.new(:path, :hunks, :status, keyword_init: true)
7
7
 
8
8
  module DiffParser
9
9
  module_function
@@ -19,10 +19,14 @@ module Sight
19
19
  if line.start_with?("diff --git ")
20
20
  current_hunk = nil
21
21
  path = line.split(" b/", 2).last
22
- current_file = DiffFile.new(path: path, hunks: [])
22
+ current_file = DiffFile.new(path: path, hunks: [], status: :modified)
23
23
  files << current_file
24
24
  elsif current_file.nil?
25
25
  next
26
+ elsif line.start_with?("new file mode")
27
+ current_file.status = :added
28
+ elsif line.start_with?("deleted file mode")
29
+ current_file.status = :deleted
26
30
  elsif line.start_with?("@@ ")
27
31
  context, new_start, old_start = parse_hunk_header(line)
28
32
  new_lineno = new_start
@@ -48,6 +52,14 @@ module Sight
48
52
  files
49
53
  end
50
54
 
55
+ def build_untracked(path, content)
56
+ lines = content.lines(chomp: true).each_with_index.map do |text, i|
57
+ DiffLine.new(type: :add, content: "+#{text}", lineno: i + 1, old_lineno: nil)
58
+ end
59
+ hunk = Hunk.new(context: nil, lines: lines)
60
+ DiffFile.new(path: path, hunks: [hunk], status: :untracked)
61
+ end
62
+
51
63
  def parse_hunk_header(line)
52
64
  match = line.match(/@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@(.*)/)
53
65
  return [nil, 1, 1] unless match
data/lib/sight/git.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "fileutils"
4
+
3
5
  module Sight
4
6
  module Git
5
7
  module_function
@@ -10,6 +12,33 @@ module Sight
10
12
  output
11
13
  end
12
14
 
15
+ def untracked_files
16
+ output, success = run_cmd(["git", "ls-files", "--others", "--exclude-standard"])
17
+ return [] unless success
18
+ output.lines(chomp: true).reject(&:empty?)
19
+ end
20
+
21
+ def file_content(path)
22
+ File.read(path, mode: "rb")
23
+ end
24
+
25
+ def repo_dir
26
+ output, success = run_cmd(["git", "rev-parse", "--git-dir"])
27
+ raise Error, "not a git repository" unless success
28
+ output.strip
29
+ end
30
+
31
+ def save_pending_review(content)
32
+ dir = File.join(repo_dir, "sight")
33
+ FileUtils.mkdir_p(dir)
34
+ File.write(File.join(dir, "pending-review"), content)
35
+ end
36
+
37
+ def clear_pending_review
38
+ path = File.join(repo_dir, "sight", "pending-review")
39
+ File.delete(path) if File.exist?(path)
40
+ end
41
+
13
42
  def run_cmd(cmd, err: IO::NULL)
14
43
  output = IO.popen(cmd, err: err, &:read)
15
44
  [output, $?.success?]
data/lib/sight/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sight
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/sight.rb CHANGED
@@ -6,6 +6,7 @@ require_relative "sight/diff_parser"
6
6
  require_relative "sight/git"
7
7
  require_relative "sight/annotation"
8
8
  require_relative "sight/annotation_formatter"
9
+ require_relative "sight/claude_hook_installer"
9
10
  require_relative "sight/cli"
10
11
  require_relative "sight/app"
11
12
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ariel Rzezak
@@ -37,12 +37,14 @@ files:
37
37
  - CODE_OF_CONDUCT.md
38
38
  - LICENSE.txt
39
39
  - README.md
40
+ - RELEASING.md
40
41
  - Rakefile
41
42
  - exe/sight
42
43
  - lib/sight.rb
43
44
  - lib/sight/annotation.rb
44
45
  - lib/sight/annotation_formatter.rb
45
46
  - lib/sight/app.rb
47
+ - lib/sight/claude_hook_installer.rb
46
48
  - lib/sight/cli.rb
47
49
  - lib/sight/diff_parser.rb
48
50
  - lib/sight/display_line.rb