githuh 0.2.1 → 0.4.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.
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Githuh
4
+ module EnvLoader
5
+ # Load a simple KEY=VALUE .env file from the current working directory
6
+ # and/or HOME, without clobbering already-set ENV entries.
7
+ def self.load!
8
+ [File.join(Dir.pwd, '.env'), File.join(Dir.home, '.env')].uniq.each do |path|
9
+ next unless File.file?(path) && File.readable?(path)
10
+
11
+ File.foreach(path) do |raw|
12
+ line = raw.strip
13
+ next if line.empty? || line.start_with?('#')
14
+
15
+ key, value = line.split('=', 2)
16
+ next if key.nil? || value.nil?
17
+
18
+ key = key.strip
19
+ value = strip_quotes(value.strip)
20
+ ENV[key] ||= value
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.strip_quotes(value)
26
+ if (value.start_with?('"') && value.end_with?('"')) ||
27
+ (value.start_with?("'") && value.end_with?("'"))
28
+ value[1..-2]
29
+ else
30
+ value
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Githuh
6
+ module LLM
7
+ class Anthropic < Base
8
+ ENDPOINT = URI('https://api.anthropic.com/v1/messages').freeze
9
+ MODEL = 'claude-haiku-4-5-20251001'
10
+ MAX_TOKENS = 800
11
+ BAR_COLOR = :yellow
12
+
13
+ def summarize(readme)
14
+ headers = {
15
+ 'x-api-key' => api_key,
16
+ 'anthropic-version' => '2023-06-01'
17
+ }
18
+ body = {
19
+ model: MODEL,
20
+ max_tokens: MAX_TOKENS,
21
+ messages: [{ role: 'user', content: prompt_for(readme) }]
22
+ }
23
+
24
+ payload = parse!(post_json(ENDPOINT, headers, body))
25
+ payload.dig('content', 0, 'text').to_s.strip
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
6
+
7
+ module Githuh
8
+ module LLM
9
+ class Base
10
+ # Hard cap to keep prompts cheap and within model context.
11
+ README_CHAR_LIMIT = 12_000
12
+ REQUEST_TIMEOUT = 30
13
+
14
+ PROMPT = <<~PROMPT
15
+ You are summarizing a GitHub repository for a public directory page.
16
+ Below is the README. Write a user-friendly description of 5–6 sentences.
17
+ Focus on: what the project *is*, what problem it *solves*, who would
18
+ use it, and any notable technical approach or feature. Keep it flowing
19
+ prose — no bullet points, no headings, no quotes, no markdown syntax.
20
+ Do NOT include installation instructions, badges, license mentions,
21
+ or author credits. Return ONLY the description prose — no preamble.
22
+
23
+ README:
24
+ ---
25
+ %<readme>s
26
+ ---
27
+ PROMPT
28
+
29
+ attr_reader :api_key, :name
30
+
31
+ def initialize(api_key:)
32
+ @api_key = api_key
33
+ @name = self.class.name.split('::').last.downcase
34
+ end
35
+
36
+ # @param readme [String] raw README markdown
37
+ # @return [String] a 2-4 sentence description
38
+ def summarize(readme)
39
+ raise NotImplementedError
40
+ end
41
+
42
+ protected
43
+
44
+ def prompt_for(readme)
45
+ format(PROMPT, readme: readme.to_s[0, README_CHAR_LIMIT])
46
+ end
47
+
48
+ def post_json(uri, headers, body)
49
+ req = Net::HTTP::Post.new(uri)
50
+ headers.each { |k, v| req[k] = v }
51
+ req['Content-Type'] = 'application/json'
52
+ req.body = JSON.dump(body)
53
+
54
+ Net::HTTP.start(uri.hostname, uri.port,
55
+ use_ssl: uri.scheme == 'https',
56
+ read_timeout: REQUEST_TIMEOUT,
57
+ open_timeout: REQUEST_TIMEOUT) do |http|
58
+ http.request(req)
59
+ end
60
+ end
61
+
62
+ def parse!(response)
63
+ raise Error, "#{name} API #{response.code}: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
64
+
65
+ JSON.parse(response.body)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Githuh
6
+ module LLM
7
+ class OpenAI < Base
8
+ ENDPOINT = URI('https://api.openai.com/v1/chat/completions').freeze
9
+ MODEL = 'gpt-4o-mini'
10
+ MAX_TOKENS = 800
11
+ BAR_COLOR = :green
12
+
13
+ def summarize(readme)
14
+ headers = { 'Authorization' => "Bearer #{api_key}" }
15
+ body = {
16
+ model: MODEL,
17
+ max_tokens: MAX_TOKENS,
18
+ messages: [{ role: 'user', content: prompt_for(readme) }]
19
+ }
20
+
21
+ payload = parse!(post_json(ENDPOINT, headers, body))
22
+ payload.dig('choices', 0, 'message', 'content').to_s.strip
23
+ end
24
+ end
25
+ end
26
+ end
data/lib/githuh/llm.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Githuh
4
+ module LLM
5
+ class Error < StandardError; end
6
+
7
+ # Returns an adapter instance based on available env vars.
8
+ # Priority: ANTHROPIC_API_KEY > OPENAI_API_KEY.
9
+ # Returns nil if no key is set (caller decides how to handle).
10
+ def self.build
11
+ if (key = env('ANTHROPIC_API_KEY'))
12
+ Anthropic.new(api_key: key)
13
+ elsif (key = env('OPENAI_API_KEY'))
14
+ OpenAI.new(api_key: key)
15
+ end
16
+ end
17
+
18
+ def self.available?
19
+ !build.nil?
20
+ end
21
+
22
+ def self.env(name)
23
+ value = ENV.fetch(name, nil)
24
+ value && !value.strip.empty? ? value.strip : nil
25
+ end
26
+ end
27
+ end
28
+
29
+ require_relative 'llm/base'
30
+ require_relative 'llm/anthropic'
31
+ require_relative 'llm/openai'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Githuh
4
- VERSION = '0.2.1'
4
+ VERSION = '0.4.0'
5
5
  end
data/lib/githuh.rb CHANGED
@@ -9,9 +9,12 @@ require 'dry/cli'
9
9
  require 'forwardable'
10
10
 
11
11
  require_relative 'githuh/version'
12
+ require_relative 'githuh/env_loader'
13
+
14
+ Githuh::EnvLoader.load!
12
15
 
13
16
  module Githuh
14
- BANNER = "Githuh Version #{VERSION}"
17
+ BANNER = "Githuh Version #{VERSION}".freeze
15
18
  BINARY = File.expand_path('../exe/githuh', __dir__).freeze
16
19
 
17
20
  module CLI
@@ -19,11 +22,9 @@ module Githuh
19
22
  extend Dry::CLI::Registry
20
23
  end
21
24
  end
22
- end
23
25
 
24
- require 'githuh/cli/launcher'
26
+ require 'githuh/cli/launcher'
25
27
 
26
- module Githuh
27
28
  class << self
28
29
  attr_accessor :launcher, :in_test
29
30
 
@@ -39,12 +40,12 @@ module Githuh
39
40
  end
40
41
 
41
42
  Kernel.module_eval do
42
- def puts(*args)
43
- ::Githuh.stdout.puts(*args)
43
+ def puts(*)
44
+ ::Githuh.stdout.puts(*)
44
45
  end
45
46
 
46
- def warn(*args)
47
- ::Githuh.stderr.puts(*args)
47
+ def warn(*)
48
+ ::Githuh.stderr.puts(*)
48
49
  end
49
50
  end
50
51
 
@@ -83,6 +84,7 @@ module Githuh
83
84
  end
84
85
  end
85
86
 
87
+ require 'githuh/llm'
86
88
  require 'githuh/cli/commands/base'
87
89
  require 'githuh/cli/commands/version'
88
90
  require 'githuh/cli/commands/user/info'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: githuh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2020-09-09 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -39,35 +38,35 @@ dependencies:
39
38
  - !ruby/object:Gem::Version
40
39
  version: '3'
41
40
  - !ruby/object:Gem::Dependency
42
- name: dry-cli
41
+ name: csv
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
- - - "~>"
44
+ - - ">="
46
45
  - !ruby/object:Gem::Version
47
- version: '0.6'
46
+ version: '0'
48
47
  type: :runtime
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
- - - "~>"
51
+ - - ">="
53
52
  - !ruby/object:Gem::Version
54
- version: '0.6'
53
+ version: '0'
55
54
  - !ruby/object:Gem::Dependency
56
- name: octokit
55
+ name: dry-cli
57
56
  requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
- - - "~>"
58
+ - - ">="
60
59
  - !ruby/object:Gem::Version
61
- version: '4'
60
+ version: '0'
62
61
  type: :runtime
63
62
  prerelease: false
64
63
  version_requirements: !ruby/object:Gem::Requirement
65
64
  requirements:
66
- - - "~>"
65
+ - - ">="
67
66
  - !ruby/object:Gem::Version
68
- version: '4'
67
+ version: '0'
69
68
  - !ruby/object:Gem::Dependency
70
- name: tty-box
69
+ name: faraday
71
70
  requirement: !ruby/object:Gem::Requirement
72
71
  requirements:
73
72
  - - ">="
@@ -81,7 +80,7 @@ dependencies:
81
80
  - !ruby/object:Gem::Version
82
81
  version: '0'
83
82
  - !ruby/object:Gem::Dependency
84
- name: tty-progressbar
83
+ name: faraday-retry
85
84
  requirement: !ruby/object:Gem::Requirement
86
85
  requirements:
87
86
  - - ">="
@@ -95,7 +94,7 @@ dependencies:
95
94
  - !ruby/object:Gem::Version
96
95
  version: '0'
97
96
  - !ruby/object:Gem::Dependency
98
- name: tty-screen
97
+ name: hashie
99
98
  requirement: !ruby/object:Gem::Requirement
100
99
  requirements:
101
100
  - - ">="
@@ -109,97 +108,27 @@ dependencies:
109
108
  - !ruby/object:Gem::Version
110
109
  version: '0'
111
110
  - !ruby/object:Gem::Dependency
112
- name: aruba
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - '='
116
- - !ruby/object:Gem::Version
117
- version: 1.0.0
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - '='
123
- - !ruby/object:Gem::Version
124
- version: 1.0.0
125
- - !ruby/object:Gem::Dependency
126
- name: awesome_print
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: '1'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - "~>"
137
- - !ruby/object:Gem::Version
138
- version: '1'
139
- - !ruby/object:Gem::Dependency
140
- name: bundler
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - "~>"
144
- - !ruby/object:Gem::Version
145
- version: '2'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - "~>"
151
- - !ruby/object:Gem::Version
152
- version: '2'
153
- - !ruby/object:Gem::Dependency
154
- name: rake
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - "~>"
158
- - !ruby/object:Gem::Version
159
- version: '13'
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - "~>"
165
- - !ruby/object:Gem::Version
166
- version: '13'
167
- - !ruby/object:Gem::Dependency
168
- name: rspec
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - "~>"
172
- - !ruby/object:Gem::Version
173
- version: '3'
174
- type: :development
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - "~>"
179
- - !ruby/object:Gem::Version
180
- version: '3'
181
- - !ruby/object:Gem::Dependency
182
- name: rspec-its
111
+ name: octokit
183
112
  requirement: !ruby/object:Gem::Requirement
184
113
  requirements:
185
- - - "~>"
114
+ - - ">="
186
115
  - !ruby/object:Gem::Version
187
- version: '1'
188
- type: :development
116
+ version: '0'
117
+ type: :runtime
189
118
  prerelease: false
190
119
  version_requirements: !ruby/object:Gem::Requirement
191
120
  requirements:
192
- - - "~>"
121
+ - - ">="
193
122
  - !ruby/object:Gem::Version
194
- version: '1'
123
+ version: '0'
195
124
  - !ruby/object:Gem::Dependency
196
- name: rubocop
125
+ name: tty-box
197
126
  requirement: !ruby/object:Gem::Requirement
198
127
  requirements:
199
128
  - - ">="
200
129
  - !ruby/object:Gem::Version
201
130
  version: '0'
202
- type: :development
131
+ type: :runtime
203
132
  prerelease: false
204
133
  version_requirements: !ruby/object:Gem::Requirement
205
134
  requirements:
@@ -207,13 +136,13 @@ dependencies:
207
136
  - !ruby/object:Gem::Version
208
137
  version: '0'
209
138
  - !ruby/object:Gem::Dependency
210
- name: simplecov
139
+ name: tty-progressbar
211
140
  requirement: !ruby/object:Gem::Requirement
212
141
  requirements:
213
142
  - - ">="
214
143
  - !ruby/object:Gem::Version
215
144
  version: '0'
216
- type: :development
145
+ type: :runtime
217
146
  prerelease: false
218
147
  version_requirements: !ruby/object:Gem::Requirement
219
148
  requirements:
@@ -221,13 +150,13 @@ dependencies:
221
150
  - !ruby/object:Gem::Version
222
151
  version: '0'
223
152
  - !ruby/object:Gem::Dependency
224
- name: simplecov-formatter-badge
153
+ name: tty-screen
225
154
  requirement: !ruby/object:Gem::Requirement
226
155
  requirements:
227
156
  - - ">="
228
157
  - !ruby/object:Gem::Version
229
158
  version: '0'
230
- type: :development
159
+ type: :runtime
231
160
  prerelease: false
232
161
  version_requirements: !ruby/object:Gem::Requirement
233
162
  requirements:
@@ -249,18 +178,24 @@ files:
249
178
  - ".rspec"
250
179
  - ".rubocop.yml"
251
180
  - ".rubocop_todo.yml"
181
+ - ".secrets.baseline"
182
+ - CHANGELOG.md
252
183
  - Gemfile
253
184
  - Gemfile.lock
254
- - LICENSE
185
+ - LICENSE.txt
255
186
  - README.adoc
256
187
  - Rakefile
257
188
  - WARRANTY.md
258
189
  - codecov.yml
190
+ - config/label-mapping.yml
191
+ - docs/img/badge.svg
192
+ - docs/img/coverage.png
259
193
  - docs/img/coverage.svg
260
194
  - docs/img/githuh-issue-export.png
261
195
  - exe/githuh
262
196
  - githuh.gemspec
263
- - issues.json
197
+ - justfile
198
+ - lefthook.yml
264
199
  - lib/githuh.rb
265
200
  - lib/githuh/cli/commands/base.rb
266
201
  - lib/githuh/cli/commands/issue/export.rb
@@ -269,28 +204,32 @@ files:
269
204
  - lib/githuh/cli/commands/user/info.rb
270
205
  - lib/githuh/cli/commands/version.rb
271
206
  - lib/githuh/cli/launcher.rb
207
+ - lib/githuh/env_loader.rb
208
+ - lib/githuh/llm.rb
209
+ - lib/githuh/llm/anthropic.rb
210
+ - lib/githuh/llm/base.rb
211
+ - lib/githuh/llm/openai.rb
272
212
  - lib/githuh/version.rb
273
213
  homepage: https://github.com/kigster/githuh
274
214
  licenses:
275
215
  - MIT
276
- metadata: {}
277
- post_install_message:
216
+ metadata:
217
+ rubygems_mfa_required: 'true'
278
218
  rdoc_options: []
279
219
  require_paths:
280
220
  - lib
281
221
  required_ruby_version: !ruby/object:Gem::Requirement
282
222
  requirements:
283
- - - ">="
223
+ - - "~>"
284
224
  - !ruby/object:Gem::Version
285
- version: '2.3'
225
+ version: '4.0'
286
226
  required_rubygems_version: !ruby/object:Gem::Requirement
287
227
  requirements:
288
228
  - - ">="
289
229
  - !ruby/object:Gem::Version
290
230
  version: '0'
291
231
  requirements: []
292
- rubygems_version: 3.1.4
293
- signing_key:
232
+ rubygems_version: 4.0.10
294
233
  specification_version: 4
295
234
  summary: Extensible CLI helper client for Github, for automating various tasks, such
296
235
  as — generating a list of org's repos and converting it into a pretty markdown,