dorian 2.1.0 → 2.2.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 +4 -4
- data/VERSION +1 -1
- data/lib/dorian/bin.rb +327 -12
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd057371121d824ad1a6172b189916820b5e5544f9cabd95db4fd7f51a2bd5fa
|
|
4
|
+
data.tar.gz: 244af12257d30dc4c617b8443a4a02d2c8ecc0fb7209f7b27dee7525ee43b35f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a6e82fef1ff6f595dfecba39e6f14ad19bf502f38b8c4af6d5e261eb69cbd3461119fbbb526089cddea881a00cb26f3b11706b34969485d2e2ad61bce6829c27
|
|
7
|
+
data.tar.gz: a3f12ae1d9d88cb6f28b975f5b59ca11ed37baa612c3e8b6c58009bc639f59084ed1af3cb2f400ed94bfc38ed8a51b7ef0c6b93aa7a1ad8118a52c8e7b7cec41
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.2.0
|
data/lib/dorian/bin.rb
CHANGED
|
@@ -5,8 +5,11 @@ require "dorian/arguments"
|
|
|
5
5
|
require "dorian/eval"
|
|
6
6
|
require "dorian/progress"
|
|
7
7
|
require "dorian/to_struct"
|
|
8
|
+
require "git"
|
|
8
9
|
require "json"
|
|
10
|
+
require "net/http"
|
|
9
11
|
require "parallel"
|
|
12
|
+
require "uri"
|
|
10
13
|
require "yaml"
|
|
11
14
|
|
|
12
15
|
class Dorian
|
|
@@ -26,7 +29,7 @@ class Dorian
|
|
|
26
29
|
"ymll" => :yamll
|
|
27
30
|
}.freeze
|
|
28
31
|
|
|
29
|
-
attr_reader :parsed, :command, :arguments, :ruby
|
|
32
|
+
attr_reader :parsed, :command, :arguments, :ruby, :ruby_before, :ruby_after
|
|
30
33
|
|
|
31
34
|
def initialize
|
|
32
35
|
@parsed =
|
|
@@ -116,15 +119,64 @@ class Dorian
|
|
|
116
119
|
when :each
|
|
117
120
|
arguments.delete("each")
|
|
118
121
|
@command = :each
|
|
119
|
-
@ruby = arguments.
|
|
120
|
-
@arguments = []
|
|
122
|
+
@ruby = arguments.delete_at(0)
|
|
121
123
|
command_each
|
|
122
124
|
when :all
|
|
123
125
|
arguments.delete("all")
|
|
124
126
|
@command = :all
|
|
125
|
-
@ruby = arguments.
|
|
126
|
-
@arguments = []
|
|
127
|
+
@ruby = arguments.delete_at(0)
|
|
127
128
|
command_all
|
|
129
|
+
when :before
|
|
130
|
+
arguments.delete("before")
|
|
131
|
+
@command = :before
|
|
132
|
+
@ruby = arguments.delete_at(0)
|
|
133
|
+
command_before
|
|
134
|
+
when :after
|
|
135
|
+
arguments.delete("after")
|
|
136
|
+
@command = :after
|
|
137
|
+
@ruby = arguments.delete_at(0)
|
|
138
|
+
command_after
|
|
139
|
+
when :between
|
|
140
|
+
arguments.delete("between")
|
|
141
|
+
@command = :between
|
|
142
|
+
@ruby_after = arguments.delete_at(0)
|
|
143
|
+
@ruby_before = arguments.delete_at(0)
|
|
144
|
+
command_between
|
|
145
|
+
when :select
|
|
146
|
+
arguments.delete("select")
|
|
147
|
+
@command = :select
|
|
148
|
+
@ruby = arguments.delete_at(0)
|
|
149
|
+
command_select
|
|
150
|
+
when :reject
|
|
151
|
+
arguments.delete("reject")
|
|
152
|
+
@command = :reject
|
|
153
|
+
@ruby = arguments.delete_at(0)
|
|
154
|
+
command_reject
|
|
155
|
+
when :tally
|
|
156
|
+
arguments.delete("tally")
|
|
157
|
+
@command = :tally
|
|
158
|
+
@ruby = arguments.delete_at(0)
|
|
159
|
+
command_tally
|
|
160
|
+
when :anonymize
|
|
161
|
+
arguments.delete("anonymize")
|
|
162
|
+
@command = :anonymize
|
|
163
|
+
command_anonymize
|
|
164
|
+
when :append
|
|
165
|
+
arguments.delete("append")
|
|
166
|
+
@command = :append
|
|
167
|
+
command_append
|
|
168
|
+
when :prepend
|
|
169
|
+
arguments.delete("prepend")
|
|
170
|
+
@command = :prepend
|
|
171
|
+
command_prepend
|
|
172
|
+
when :chat
|
|
173
|
+
arguments.delete("chat")
|
|
174
|
+
@command = :chat
|
|
175
|
+
command_chat
|
|
176
|
+
when :commit
|
|
177
|
+
arguments.delete("commit")
|
|
178
|
+
@command = :commit
|
|
179
|
+
command_commit
|
|
128
180
|
else
|
|
129
181
|
arguments.delete("read")
|
|
130
182
|
@command = :read
|
|
@@ -136,6 +188,43 @@ class Dorian
|
|
|
136
188
|
parsed.files
|
|
137
189
|
end
|
|
138
190
|
|
|
191
|
+
def command_chat
|
|
192
|
+
puts completion(
|
|
193
|
+
token: token(".chat"),
|
|
194
|
+
model: "gpt-4o",
|
|
195
|
+
messages: [{ role: :user, content: everything.join("\n") }]
|
|
196
|
+
)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def command_commit
|
|
200
|
+
system_prompt = "simple, clear, short, lowercase commit message"
|
|
201
|
+
prompt_1 = "for the following diff:"
|
|
202
|
+
prompt_2 = "for the following git status:"
|
|
203
|
+
prompt_3 = "for the following comment:"
|
|
204
|
+
|
|
205
|
+
content_1 = short(`git diff --staged`)
|
|
206
|
+
content_2 = short(`git status`)
|
|
207
|
+
content_3 = short(arguments.join("\n"))
|
|
208
|
+
|
|
209
|
+
abort "no staged files" if content_1.empty?
|
|
210
|
+
|
|
211
|
+
messages = [
|
|
212
|
+
{ role: :system, content: system_prompt },
|
|
213
|
+
{ role: :system, content: prompt_1 },
|
|
214
|
+
{ role: :user, content: content_1 },
|
|
215
|
+
{ role: :system, content: prompt_2 },
|
|
216
|
+
{ role: :user, content: content_2 },
|
|
217
|
+
{ role: :system, content: prompt_3 },
|
|
218
|
+
{ role: :user, content: content_3 }
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
message = completion(token: token(".commit"), model: "gpt-4o", messages: messages)
|
|
222
|
+
|
|
223
|
+
Git.open(".").commit(message)
|
|
224
|
+
|
|
225
|
+
puts message
|
|
226
|
+
end
|
|
227
|
+
|
|
139
228
|
def command_read
|
|
140
229
|
each(stdin_files + files) do |input|
|
|
141
230
|
outputs(reads(File.read(input)), file: input)
|
|
@@ -145,20 +234,108 @@ class Dorian
|
|
|
145
234
|
end
|
|
146
235
|
|
|
147
236
|
def everything
|
|
148
|
-
read_stdin_files +
|
|
237
|
+
read_stdin_files + stdin_arguments + read_files + arguments
|
|
149
238
|
end
|
|
150
239
|
|
|
151
240
|
def command_each
|
|
152
241
|
each(everything) do |input|
|
|
153
|
-
each(lines(reads(input)), progress: true)
|
|
154
|
-
|
|
155
|
-
|
|
242
|
+
each(lines(reads(input)), progress: true) { |line| evaluates(it: line) }
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def command_tally
|
|
247
|
+
each(everything) do |input|
|
|
248
|
+
outputs(
|
|
249
|
+
JSON.pretty_generate(
|
|
250
|
+
map(lines(reads(input)), progress: true) do |element|
|
|
251
|
+
if ruby.to_s.empty?
|
|
252
|
+
element
|
|
253
|
+
else
|
|
254
|
+
evaluates(it: element, returns: true, stdout: false).returned
|
|
255
|
+
end
|
|
256
|
+
end.tally
|
|
257
|
+
)
|
|
258
|
+
)
|
|
156
259
|
end
|
|
157
260
|
end
|
|
158
261
|
|
|
159
262
|
def command_all
|
|
160
|
-
each(everything, progress: true)
|
|
161
|
-
|
|
263
|
+
each(everything, progress: true) { |input| evaluates(it: reads(input)) }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def command_append
|
|
267
|
+
outputs(everything.map { |input| lines(reads(input)) }.inject(&:+))
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def command_prepend
|
|
271
|
+
outputs(
|
|
272
|
+
everything.reverse.map { |input| lines(reads(input)) }.inject(&:+)
|
|
273
|
+
)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def command_select
|
|
277
|
+
each(stdin_files + files) do |input|
|
|
278
|
+
outputs(
|
|
279
|
+
select(lines(reads(File.read(input)))) { |element| match?(element) },
|
|
280
|
+
file: input
|
|
281
|
+
)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
each(stdin_arguments + arguments) do |input|
|
|
285
|
+
outputs(select(lines(reads(input))) { |element| match?(element) })
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def command_reject
|
|
290
|
+
each(stdin_files + files) do |input|
|
|
291
|
+
outputs(
|
|
292
|
+
reject(lines(reads(File.read(input)))) { |element| match?(element) },
|
|
293
|
+
file: input
|
|
294
|
+
)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
each(stdin_arguments + arguments) do |input|
|
|
298
|
+
outputs(reject(lines(reads(input))) { |element| match?(element) })
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def command_after
|
|
303
|
+
each(stdin_files + files) do |input|
|
|
304
|
+
outputs(after(lines(reads(File.read(input)))), file: input)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
each(stdin_arguments + arguments) do |input|
|
|
308
|
+
outputs(after(lines(reads(input))))
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def command_before
|
|
313
|
+
each(stdin_files + files) do |input|
|
|
314
|
+
outputs(before(reads(File.read(input))), file: input)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
each(stdin_arguments + arguments) do |input|
|
|
318
|
+
outputs(before(lines(reads(input))))
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def command_between
|
|
323
|
+
each(stdin_files + files) do |input|
|
|
324
|
+
outputs(between(lines(reads(File.read(input)))), file: input)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
each(stdin_arguments + arguments) do |input|
|
|
328
|
+
outputs(between(lines(reads(input))))
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def command_anonymize
|
|
333
|
+
each(stdin_files + files) do |input|
|
|
334
|
+
outputs(anonymize(reads(File.read(input))), file: input)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
each(stdin_arguments + arguments) do |input|
|
|
338
|
+
outputs(anonymize(reads(input)))
|
|
162
339
|
end
|
|
163
340
|
end
|
|
164
341
|
|
|
@@ -400,6 +577,24 @@ class Dorian
|
|
|
400
577
|
end
|
|
401
578
|
end
|
|
402
579
|
|
|
580
|
+
def select(collection, progress: false, &)
|
|
581
|
+
collection = wrap(collection)
|
|
582
|
+
progress_bar = progress ? create_progress_bar(collection.size) : nil
|
|
583
|
+
|
|
584
|
+
collection.select do |element|
|
|
585
|
+
yield(element).tap { progress_bar&.increment }
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def reject(collection, progress: false, &)
|
|
590
|
+
collection = wrap(collection)
|
|
591
|
+
progress_bar = progress ? create_progress_bar(collection.size) : nil
|
|
592
|
+
|
|
593
|
+
collection.reject do |element|
|
|
594
|
+
yield(element).tap { progress_bar&.increment }
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
403
598
|
def lines(input)
|
|
404
599
|
if input.is_a?(String)
|
|
405
600
|
input.lines.map(&:rstrip)
|
|
@@ -441,8 +636,128 @@ class Dorian
|
|
|
441
636
|
Dorian::Progress.create(total:, format: progress_format)
|
|
442
637
|
end
|
|
443
638
|
|
|
639
|
+
def after(input, ruby: @ruby_after || @ruby)
|
|
640
|
+
if ruby.to_i.to_s == ruby
|
|
641
|
+
input[(ruby.to_i)..]
|
|
642
|
+
else
|
|
643
|
+
selected = false
|
|
644
|
+
|
|
645
|
+
input.select do |element|
|
|
646
|
+
selected = true if match?(element, ruby:)
|
|
647
|
+
selected
|
|
648
|
+
end
|
|
649
|
+
end
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
def before(input, ruby: @ruby_before || @ruby)
|
|
653
|
+
if ruby.to_i.to_s == ruby
|
|
654
|
+
input[..(ruby.to_i)]
|
|
655
|
+
else
|
|
656
|
+
selected = true
|
|
657
|
+
|
|
658
|
+
input.select do |element|
|
|
659
|
+
selected.tap { selected = false if match?(element, ruby:) }
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def between(input, ruby_before: @ruby_before, ruby_after: @ruby_after)
|
|
665
|
+
if ruby_before.to_i.to_s == ruby_before &&
|
|
666
|
+
ruby_after.to_i.to_s == ruby_after
|
|
667
|
+
input[(ruby_after.to_i)..(ruby_before.to_i)]
|
|
668
|
+
else
|
|
669
|
+
selected = false
|
|
670
|
+
|
|
671
|
+
input.select do |element|
|
|
672
|
+
selected = true if match?(element, ruby: ruby_after)
|
|
673
|
+
selected.tap do
|
|
674
|
+
selected = false if match?(element, ruby: ruby_before)
|
|
675
|
+
end
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
def anonymize(input)
|
|
681
|
+
if input.is_a?(String)
|
|
682
|
+
input.gsub(/[a-z]/, "a").gsub(/[A-Z]/, "A").gsub(/[0-9]/, "0")
|
|
683
|
+
elsif input.is_a?(Integer)
|
|
684
|
+
0
|
|
685
|
+
elsif input.is_a?(Float)
|
|
686
|
+
0.0
|
|
687
|
+
elsif input.is_a?(TrueClass) || input.is_a?(FalseClass)
|
|
688
|
+
false
|
|
689
|
+
elsif input.nil?
|
|
690
|
+
nil
|
|
691
|
+
elsif input.is_a?(Hash)
|
|
692
|
+
input.transform_values { |value| anonymize(value) }
|
|
693
|
+
elsif input.is_a?(Array)
|
|
694
|
+
input.map { |element| anonymize(element) }
|
|
695
|
+
elsif input.is_a?(Struct)
|
|
696
|
+
anonymize(input.from_deep_struct).to_deep_struct
|
|
697
|
+
else
|
|
698
|
+
raise "#{input.class.inspect} not supported"
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
def match?(element, ruby: @ruby)
|
|
703
|
+
!!evaluates(ruby:, it: element, stdout: false, returns: true).returned
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
def token(file)
|
|
707
|
+
token_file = File.join(Dir.home, file)
|
|
708
|
+
|
|
709
|
+
if File.exist?(token_file)
|
|
710
|
+
token = File.read(token_file).strip
|
|
711
|
+
else
|
|
712
|
+
print "token: "
|
|
713
|
+
token = gets.strip
|
|
714
|
+
File.write(token_file, token)
|
|
715
|
+
puts "token written to #{token_file}"
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
token
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
def completion(token:, model:, messages:)
|
|
722
|
+
body =
|
|
723
|
+
post(
|
|
724
|
+
"https://api.openai.com/v1/chat/completions",
|
|
725
|
+
headers: {
|
|
726
|
+
"Content-Type" => "application/json",
|
|
727
|
+
"Authorization" => "Bearer #{token}"
|
|
728
|
+
},
|
|
729
|
+
body: { model:, messages: }.to_json
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
json = JSON.parse(body)
|
|
733
|
+
output = json.dig("choices", 0, "message", "content")
|
|
734
|
+
|
|
735
|
+
if output
|
|
736
|
+
output.strip
|
|
737
|
+
else
|
|
738
|
+
abort JSON.pretty_generate(json)
|
|
739
|
+
end
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
def post(url, headers: {}, body: {})
|
|
743
|
+
uri = URI.parse(url)
|
|
744
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
745
|
+
http.use_ssl = true
|
|
746
|
+
request = Net::HTTP::Post.new(uri.path, headers)
|
|
747
|
+
request.body = body
|
|
748
|
+
http.request(request).body
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
def short(string)
|
|
752
|
+
string[0..5000]
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
def encoder
|
|
756
|
+
Tiktoken.encoding_for_model("gpt-4o")
|
|
757
|
+
end
|
|
758
|
+
|
|
444
759
|
def evaluates(
|
|
445
|
-
ruby,
|
|
760
|
+
ruby: @ruby,
|
|
446
761
|
it: nil,
|
|
447
762
|
debug: debug?,
|
|
448
763
|
stdout: stdout?,
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dorian
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dorian Marié
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-09-
|
|
11
|
+
date: 2024-09-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: csv
|
|
@@ -80,6 +80,20 @@ dependencies:
|
|
|
80
80
|
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: git
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
83
97
|
- !ruby/object:Gem::Dependency
|
|
84
98
|
name: json
|
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|