brainiac 0.0.25 → 0.0.26
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/Gemfile.lock +2 -2
- data/bin/brainiac +261 -35
- data/bin/brainiac-completion.bash +12 -1
- data/lib/brainiac/handlers/shared/git.rb +25 -0
- data/lib/brainiac/version.rb +1 -1
- data/receiver.rb +6 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 963f3a3319ca2ccff44fc003072a0566d3b76d06a42ea645d81693bb48ea2ab3
|
|
4
|
+
data.tar.gz: 7abb9b38680549aaf3da6068b459f0faf474f5862f7f235f1dabddfcb38d5e55
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dae8e213cb21a26f97de1406a5fe0bc1ce7dc5806e3b67079e2c0183b94888439f288243d86dc52fd9831d1ee61294a2f28fe9fe4f88dc03819c1d720a95db9b
|
|
7
|
+
data.tar.gz: 2e7cf53a24fd776e2eb7960c379dc49309d932412f5fe39a8a56dd90a6162dcf3a9924396a7386b67a0288c0034381343effb529e53b8bcf4b4f43b7fc454708
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
brainiac (0.0.
|
|
4
|
+
brainiac (0.0.26)
|
|
5
5
|
puma (~> 7.2)
|
|
6
6
|
rackup (~> 2.3)
|
|
7
7
|
sinatra (~> 4.1)
|
|
@@ -84,7 +84,7 @@ DEPENDENCIES
|
|
|
84
84
|
CHECKSUMS
|
|
85
85
|
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
86
86
|
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
87
|
-
brainiac (0.0.
|
|
87
|
+
brainiac (0.0.26)
|
|
88
88
|
json (2.19.9) sha256=9b9025b7cdddafa38d316eca0b2358488e42d417045c1b90d216a9fefe46b79a
|
|
89
89
|
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
|
90
90
|
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
data/bin/brainiac
CHANGED
|
@@ -208,14 +208,7 @@ end
|
|
|
208
208
|
def stop_server
|
|
209
209
|
pid = find_server_pid
|
|
210
210
|
|
|
211
|
-
|
|
212
|
-
puts "No running Brainiac server found."
|
|
213
|
-
return false
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
puts "Stopping Brainiac server (PID: #{pid})..."
|
|
217
|
-
|
|
218
|
-
# Also stop the monitor daemon
|
|
211
|
+
# Stop the monitor daemon regardless of server PID
|
|
219
212
|
daemon_pid_file = "/tmp/brainiac-daemon.pid"
|
|
220
213
|
if File.exist?(daemon_pid_file)
|
|
221
214
|
daemon_pid = File.read(daemon_pid_file).strip.to_i
|
|
@@ -228,40 +221,61 @@ def stop_server
|
|
|
228
221
|
File.delete(daemon_pid_file)
|
|
229
222
|
end
|
|
230
223
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
sleep 2
|
|
237
|
-
next if process_running?(pid)
|
|
224
|
+
# Kill the known server PID
|
|
225
|
+
if pid
|
|
226
|
+
puts "Stopping Brainiac server (PID: #{pid})..."
|
|
227
|
+
kill_pid(pid)
|
|
228
|
+
end
|
|
238
229
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
230
|
+
# After killing the known PID, find anything still holding port 4567.
|
|
231
|
+
# This catches orphaned processes from self-restarts, stale PIDs, or child processes.
|
|
232
|
+
lingering = find_port_holders(4567) - [pid].compact
|
|
233
|
+
if lingering.any?
|
|
234
|
+
puts "Cleaning up #{lingering.size} orphaned process(es) on port 4567..."
|
|
235
|
+
lingering.each { |lpid| kill_pid(lpid, quiet: true) }
|
|
236
|
+
end
|
|
243
237
|
|
|
244
|
-
|
|
245
|
-
puts "Server didn't stop gracefully, forcing..."
|
|
246
|
-
Process.kill("KILL", pid)
|
|
247
|
-
sleep 1
|
|
238
|
+
FileUtils.rm_f(PID_FILE)
|
|
248
239
|
|
|
249
|
-
|
|
250
|
-
puts "✓ Server stopped
|
|
240
|
+
if pid || lingering.any?
|
|
241
|
+
puts "✓ Server stopped."
|
|
251
242
|
true
|
|
252
|
-
|
|
253
|
-
puts "
|
|
254
|
-
FileUtils.rm_f(PID_FILE)
|
|
255
|
-
true
|
|
256
|
-
rescue Errno::EPERM
|
|
257
|
-
puts "Error: Permission denied. Try running with sudo or check process ownership."
|
|
258
|
-
false
|
|
259
|
-
rescue StandardError => e
|
|
260
|
-
puts "Error stopping server: #{e.message}"
|
|
243
|
+
else
|
|
244
|
+
puts "No running Brainiac server found."
|
|
261
245
|
false
|
|
262
246
|
end
|
|
263
247
|
end
|
|
264
248
|
|
|
249
|
+
# Kill a single process: TERM first, KILL if it doesn't exit within 5s.
|
|
250
|
+
def kill_pid(pid, quiet: false)
|
|
251
|
+
Process.kill("TERM", pid)
|
|
252
|
+
|
|
253
|
+
5.times do
|
|
254
|
+
sleep 1
|
|
255
|
+
return true unless process_running?(pid)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
puts " PID #{pid} didn't stop gracefully, forcing..." unless quiet
|
|
259
|
+
Process.kill("KILL", pid)
|
|
260
|
+
sleep 1
|
|
261
|
+
true
|
|
262
|
+
rescue Errno::ESRCH
|
|
263
|
+
true # Already dead
|
|
264
|
+
rescue Errno::EPERM
|
|
265
|
+
puts "Error: Permission denied to stop PID #{pid}." unless quiet
|
|
266
|
+
false
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Find all PIDs listening on a given port via lsof.
|
|
270
|
+
def find_port_holders(port)
|
|
271
|
+
output, status = Open3.capture2("lsof", "-ti", ":#{port}")
|
|
272
|
+
return [] unless status.success?
|
|
273
|
+
|
|
274
|
+
output.strip.split("\n").map(&:to_i).select(&:positive?)
|
|
275
|
+
rescue StandardError
|
|
276
|
+
[]
|
|
277
|
+
end
|
|
278
|
+
|
|
265
279
|
def update_handler_config(name, enabled)
|
|
266
280
|
brainiac_config_path = File.join(BRAINIAC_DIR, "brainiac.json")
|
|
267
281
|
config = if File.exist?(brainiac_config_path)
|
|
@@ -3372,16 +3386,227 @@ when "plugin"
|
|
|
3372
3386
|
puts " brainiac install #{plugin_name} --path #{output_dir}"
|
|
3373
3387
|
puts " brainiac restart"
|
|
3374
3388
|
|
|
3389
|
+
when "list", "ls"
|
|
3390
|
+
# Delegate to the same logic as `brainiac plugins`
|
|
3391
|
+
plugins_file = File.join(BRAINIAC_DIR, "plugins.json")
|
|
3392
|
+
plugins_config = if File.exist?(plugins_file)
|
|
3393
|
+
JSON.parse(File.read(plugins_file))
|
|
3394
|
+
else
|
|
3395
|
+
{ "plugins" => [] }
|
|
3396
|
+
end
|
|
3397
|
+
|
|
3398
|
+
plugins = plugins_config["plugins"] || []
|
|
3399
|
+
if plugins.empty?
|
|
3400
|
+
puts "No plugins installed."
|
|
3401
|
+
puts ""
|
|
3402
|
+
puts "Install plugins with: brainiac install <name>"
|
|
3403
|
+
puts " Example: brainiac install whatsapp"
|
|
3404
|
+
puts " Example: brainiac install fizzy --path ~/Code/brainiac-fizzy"
|
|
3405
|
+
else
|
|
3406
|
+
puts "Installed plugins:"
|
|
3407
|
+
plugins.each do |p|
|
|
3408
|
+
entry = p.is_a?(Hash) ? p : { "name" => p.to_s }
|
|
3409
|
+
name = entry["name"]
|
|
3410
|
+
gem_name = entry["gem"] || "brainiac-#{name}"
|
|
3411
|
+
local_path = entry["path"]
|
|
3412
|
+
installed_at = entry["installed_at"] ? " (#{entry["installed_at"][0..9]})" : ""
|
|
3413
|
+
|
|
3414
|
+
if local_path
|
|
3415
|
+
if Dir.exist?(local_path)
|
|
3416
|
+
puts " ✓ #{name} → #{local_path}#{installed_at}"
|
|
3417
|
+
else
|
|
3418
|
+
puts " ✗ #{name} → #{local_path} (path missing)#{installed_at}"
|
|
3419
|
+
end
|
|
3420
|
+
else
|
|
3421
|
+
loadable = begin
|
|
3422
|
+
spec = Gem::Specification.find_by_name(gem_name)
|
|
3423
|
+
"✓ #{name} (#{gem_name} #{spec.version})"
|
|
3424
|
+
rescue Gem::MissingSpecError
|
|
3425
|
+
"✗ #{name} (#{gem_name} — gem not found)"
|
|
3426
|
+
end
|
|
3427
|
+
puts " #{loadable}#{installed_at}"
|
|
3428
|
+
end
|
|
3429
|
+
end
|
|
3430
|
+
end
|
|
3431
|
+
|
|
3432
|
+
when "switch"
|
|
3433
|
+
plugin_name = ARGV.shift
|
|
3434
|
+
branch_name = ARGV.shift
|
|
3435
|
+
|
|
3436
|
+
unless plugin_name && branch_name
|
|
3437
|
+
puts "Usage: brainiac plugin switch <name> <branch>"
|
|
3438
|
+
puts ""
|
|
3439
|
+
puts "Switches a local plugin to a different branch (via git worktrees)."
|
|
3440
|
+
puts "Useful for testing plugin changes from a PR before releasing."
|
|
3441
|
+
puts ""
|
|
3442
|
+
puts "Examples:"
|
|
3443
|
+
puts " brainiac plugin switch fizzy fix-fallback-for-followups-20260813"
|
|
3444
|
+
puts " brainiac plugin switch fizzy main # switch back to main"
|
|
3445
|
+
puts ""
|
|
3446
|
+
puts "After switching, restart the server: brainiac restart"
|
|
3447
|
+
exit 1
|
|
3448
|
+
end
|
|
3449
|
+
|
|
3450
|
+
plugin_name = plugin_name.sub(/^brainiac-/, "")
|
|
3451
|
+
|
|
3452
|
+
plugins_file = File.join(BRAINIAC_DIR, "plugins.json")
|
|
3453
|
+
unless File.exist?(plugins_file)
|
|
3454
|
+
puts "Error: No plugins installed (plugins.json not found)."
|
|
3455
|
+
exit 1
|
|
3456
|
+
end
|
|
3457
|
+
|
|
3458
|
+
plugins_config = JSON.parse(File.read(plugins_file))
|
|
3459
|
+
plugin_idx = (plugins_config["plugins"] || []).index { |p| (p.is_a?(Hash) ? p["name"] : p.to_s) == plugin_name }
|
|
3460
|
+
|
|
3461
|
+
unless plugin_idx
|
|
3462
|
+
puts "Error: Plugin '#{plugin_name}' is not installed."
|
|
3463
|
+
exit 1
|
|
3464
|
+
end
|
|
3465
|
+
|
|
3466
|
+
entry = plugins_config["plugins"][plugin_idx]
|
|
3467
|
+
unless entry.is_a?(Hash) && entry["path"]
|
|
3468
|
+
puts "Error: Plugin '#{plugin_name}' is not a local-path plugin."
|
|
3469
|
+
puts " Only plugins installed with --path support branch switching."
|
|
3470
|
+
exit 1
|
|
3471
|
+
end
|
|
3472
|
+
|
|
3473
|
+
current_path = entry["path"]
|
|
3474
|
+
gem_name = "brainiac-#{plugin_name}"
|
|
3475
|
+
|
|
3476
|
+
# Determine the base repo path (strip worktree suffix like --branch-name)
|
|
3477
|
+
base_path = current_path.sub(/--[^\/]+$/, "")
|
|
3478
|
+
unless Dir.exist?(base_path)
|
|
3479
|
+
puts "Error: Base repo not found at #{base_path}"
|
|
3480
|
+
exit 1
|
|
3481
|
+
end
|
|
3482
|
+
|
|
3483
|
+
# Get the default branch of the repo
|
|
3484
|
+
default_branch, _, db_status = Open3.capture3("git", "symbolic-ref", "refs/remotes/origin/HEAD", "--short", chdir: base_path)
|
|
3485
|
+
default_branch = default_branch.strip.sub("origin/", "") if db_status.success?
|
|
3486
|
+
default_branch = "main" unless db_status.success? && !default_branch.empty?
|
|
3487
|
+
|
|
3488
|
+
# If switching to the default branch, just point back to the base path
|
|
3489
|
+
if branch_name == default_branch
|
|
3490
|
+
if current_path == base_path
|
|
3491
|
+
puts "Plugin '#{plugin_name}' is already on #{default_branch}."
|
|
3492
|
+
exit 0
|
|
3493
|
+
end
|
|
3494
|
+
|
|
3495
|
+
plugins_config["plugins"][plugin_idx]["path"] = base_path
|
|
3496
|
+
File.write(plugins_file, JSON.pretty_generate(plugins_config))
|
|
3497
|
+
puts "✓ Switched plugin '#{plugin_name}' back to #{default_branch}"
|
|
3498
|
+
puts " Path: #{base_path}"
|
|
3499
|
+
puts " Restart the server to apply: brainiac restart"
|
|
3500
|
+
exit 0
|
|
3501
|
+
end
|
|
3502
|
+
|
|
3503
|
+
# Fetch latest to make sure the branch is available
|
|
3504
|
+
puts "Fetching latest from origin..."
|
|
3505
|
+
_out, _err, fetch_status = Open3.capture3("git", "fetch", "origin", chdir: base_path)
|
|
3506
|
+
unless fetch_status.success?
|
|
3507
|
+
puts "Warning: git fetch failed — continuing with local state."
|
|
3508
|
+
end
|
|
3509
|
+
|
|
3510
|
+
# Check if a worktree already exists for this branch
|
|
3511
|
+
worktree_list, _, wt_status = Open3.capture3("git", "worktree", "list", "--porcelain", chdir: base_path)
|
|
3512
|
+
target_worktree = nil
|
|
3513
|
+
|
|
3514
|
+
if wt_status.success?
|
|
3515
|
+
# Parse porcelain output to find worktrees on the target branch
|
|
3516
|
+
worktrees = worktree_list.split("\n\n").map do |block|
|
|
3517
|
+
wt = {}
|
|
3518
|
+
block.each_line do |line|
|
|
3519
|
+
case line
|
|
3520
|
+
when /^worktree (.+)/
|
|
3521
|
+
wt[:path] = Regexp.last_match(1).strip
|
|
3522
|
+
when /^branch refs\/heads\/(.+)/
|
|
3523
|
+
wt[:branch] = Regexp.last_match(1).strip
|
|
3524
|
+
end
|
|
3525
|
+
end
|
|
3526
|
+
wt
|
|
3527
|
+
end.select { |wt| wt[:path] && wt[:branch] }
|
|
3528
|
+
|
|
3529
|
+
target_worktree = worktrees.find { |wt| wt[:branch] == branch_name }
|
|
3530
|
+
end
|
|
3531
|
+
|
|
3532
|
+
if target_worktree
|
|
3533
|
+
# Worktree already exists — just update the path
|
|
3534
|
+
new_path = target_worktree[:path]
|
|
3535
|
+
if current_path == new_path
|
|
3536
|
+
puts "Plugin '#{plugin_name}' is already on branch '#{branch_name}'."
|
|
3537
|
+
exit 0
|
|
3538
|
+
end
|
|
3539
|
+
|
|
3540
|
+
plugins_config["plugins"][plugin_idx]["path"] = new_path
|
|
3541
|
+
File.write(plugins_file, JSON.pretty_generate(plugins_config))
|
|
3542
|
+
puts "✓ Switched plugin '#{plugin_name}' to branch '#{branch_name}'"
|
|
3543
|
+
puts " Path: #{new_path}"
|
|
3544
|
+
puts " Restart the server to apply: brainiac restart"
|
|
3545
|
+
else
|
|
3546
|
+
# Check if branch exists remotely or locally
|
|
3547
|
+
branch_exists = false
|
|
3548
|
+
check_cmd, _, check_status = Open3.capture3("git", "rev-parse", "--verify", "origin/#{branch_name}", chdir: base_path)
|
|
3549
|
+
branch_exists = check_status.success?
|
|
3550
|
+
|
|
3551
|
+
unless branch_exists
|
|
3552
|
+
# Try local branch
|
|
3553
|
+
_, _, local_status = Open3.capture3("git", "rev-parse", "--verify", branch_name, chdir: base_path)
|
|
3554
|
+
branch_exists = local_status.success?
|
|
3555
|
+
end
|
|
3556
|
+
|
|
3557
|
+
unless branch_exists
|
|
3558
|
+
puts "Error: Branch '#{branch_name}' not found (checked local and origin)."
|
|
3559
|
+
puts ""
|
|
3560
|
+
puts "Available remote branches:"
|
|
3561
|
+
branches_out, = Open3.capture3("git", "branch", "-r", "--list", "origin/*", chdir: base_path)
|
|
3562
|
+
branches_out.each_line do |line|
|
|
3563
|
+
branch = line.strip.sub("origin/", "")
|
|
3564
|
+
next if branch.include?("HEAD")
|
|
3565
|
+
|
|
3566
|
+
puts " #{branch}"
|
|
3567
|
+
end
|
|
3568
|
+
exit 1
|
|
3569
|
+
end
|
|
3570
|
+
|
|
3571
|
+
# Create a new worktree
|
|
3572
|
+
worktree_dir = "#{base_path}--#{branch_name}"
|
|
3573
|
+
puts "Creating worktree at #{worktree_dir}..."
|
|
3574
|
+
|
|
3575
|
+
# If the branch exists on origin but not locally, track it
|
|
3576
|
+
_, _, local_check = Open3.capture3("git", "rev-parse", "--verify", branch_name, chdir: base_path)
|
|
3577
|
+
if !local_check.success? && check_status.success?
|
|
3578
|
+
# Remote branch exists but not local — create tracking branch in worktree
|
|
3579
|
+
_, stderr, wt_status = Open3.capture3("git", "worktree", "add", "--track", "-b", branch_name, worktree_dir, "origin/#{branch_name}", chdir: base_path)
|
|
3580
|
+
else
|
|
3581
|
+
# Local branch exists — use it directly
|
|
3582
|
+
_, stderr, wt_status = Open3.capture3("git", "worktree", "add", worktree_dir, branch_name, chdir: base_path)
|
|
3583
|
+
end
|
|
3584
|
+
|
|
3585
|
+
unless wt_status.success?
|
|
3586
|
+
puts "Error: Failed to create worktree:"
|
|
3587
|
+
puts " #{stderr.strip}"
|
|
3588
|
+
exit 1
|
|
3589
|
+
end
|
|
3590
|
+
|
|
3591
|
+
plugins_config["plugins"][plugin_idx]["path"] = worktree_dir
|
|
3592
|
+
File.write(plugins_file, JSON.pretty_generate(plugins_config))
|
|
3593
|
+
puts "✓ Switched plugin '#{plugin_name}' to branch '#{branch_name}'"
|
|
3594
|
+
puts " Worktree: #{worktree_dir}"
|
|
3595
|
+
puts " Restart the server to apply: brainiac restart"
|
|
3596
|
+
end
|
|
3597
|
+
|
|
3375
3598
|
else
|
|
3376
3599
|
puts "Usage: brainiac plugin <command>"
|
|
3377
3600
|
puts ""
|
|
3378
3601
|
puts "Commands:"
|
|
3602
|
+
puts " list List installed plugins"
|
|
3379
3603
|
puts " new <name> Generate a new plugin gem skeleton"
|
|
3604
|
+
puts " switch <name> <branch> Switch a local plugin to a different branch"
|
|
3380
3605
|
puts ""
|
|
3381
3606
|
puts "Managing installed plugins:"
|
|
3382
3607
|
puts " brainiac install <name> Install a plugin"
|
|
3383
3608
|
puts " brainiac uninstall <name> Remove a plugin"
|
|
3384
|
-
puts " brainiac plugins List installed plugins"
|
|
3609
|
+
puts " brainiac plugins List installed plugins (alias)"
|
|
3385
3610
|
end
|
|
3386
3611
|
|
|
3387
3612
|
when "screenshot"
|
|
@@ -3613,6 +3838,7 @@ when "help", "--help", "-h", nil
|
|
|
3613
3838
|
|
|
3614
3839
|
Plugin Commands:
|
|
3615
3840
|
plugin new <name> Generate a new plugin gem skeleton
|
|
3841
|
+
plugin switch <name> <branch> Switch a local plugin to a branch (for testing)
|
|
3616
3842
|
install <name> Install a plugin (gem install brainiac-<name>)
|
|
3617
3843
|
uninstall <name> Remove a plugin
|
|
3618
3844
|
update [name] Update brainiac + all plugins, or a specific plugin
|
|
@@ -63,7 +63,18 @@ _brainiac() {
|
|
|
63
63
|
plugin)
|
|
64
64
|
case $cword in
|
|
65
65
|
2)
|
|
66
|
-
COMPREPLY=($(compgen -W "new" -- "$cur"))
|
|
66
|
+
COMPREPLY=($(compgen -W "new switch list" -- "$cur"))
|
|
67
|
+
;;
|
|
68
|
+
3)
|
|
69
|
+
# For "plugin switch", suggest installed plugin names
|
|
70
|
+
if [[ "${COMP_WORDS[2]}" == "switch" && -f "$brainiac_dir/plugins.json" ]]; then
|
|
71
|
+
local installed
|
|
72
|
+
installed=$(ruby -rjson -e '
|
|
73
|
+
config = JSON.parse(File.read(ARGV[0]))
|
|
74
|
+
(config["plugins"] || []).select { |p| p.is_a?(Hash) && p["path"] }.each { |p| puts p["name"] }
|
|
75
|
+
' "$brainiac_dir/plugins.json" 2>/dev/null)
|
|
76
|
+
COMPREPLY=($(compgen -W "$installed" -- "$cur"))
|
|
77
|
+
fi
|
|
67
78
|
;;
|
|
68
79
|
esac
|
|
69
80
|
;;
|
|
@@ -4,6 +4,31 @@
|
|
|
4
4
|
#
|
|
5
5
|
# All git-related utilities live here. Handlers call these instead of
|
|
6
6
|
# reimplementing git worktree/branch logic.
|
|
7
|
+
#
|
|
8
|
+
# NOTE: Methods that need to be accessible from Sinatra route handlers
|
|
9
|
+
# are wrapped in GitHelpers module and registered with Sinatra.
|
|
10
|
+
|
|
11
|
+
# Module containing git helpers that need to be accessible from Sinatra routes.
|
|
12
|
+
# These are registered as Sinatra helpers in receiver.rb so plugins can call them.
|
|
13
|
+
module GitHelpers
|
|
14
|
+
def resolve_base_branch(repo_path:, card_number: nil, project_key: nil)
|
|
15
|
+
results = Brainiac.emit(:resolve_base_branch,
|
|
16
|
+
repo_path: repo_path, card_number: card_number, project_key: project_key)
|
|
17
|
+
custom = results.compact.first
|
|
18
|
+
if custom
|
|
19
|
+
LOG.info "Using custom base branch '#{custom}' (from plugin hook)"
|
|
20
|
+
custom
|
|
21
|
+
else
|
|
22
|
+
"origin/#{get_default_branch(repo_path)}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def resolve_pr_target(repo_path:, card_number: nil, project_key: nil)
|
|
27
|
+
results = Brainiac.emit(:resolve_pr_target,
|
|
28
|
+
repo_path: repo_path, card_number: card_number, project_key: project_key)
|
|
29
|
+
results.compact.first
|
|
30
|
+
end
|
|
31
|
+
end
|
|
7
32
|
|
|
8
33
|
# Debounced repo git fetch — avoids fetching the same repo multiple times within a short window.
|
|
9
34
|
REPO_LAST_FETCH = {}
|
data/lib/brainiac/version.rb
CHANGED
data/receiver.rb
CHANGED
|
@@ -114,8 +114,13 @@ else
|
|
|
114
114
|
LOG.info "[Intent] Disabled (enable in brainiac.json → intent.enabled: true)"
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
-
# ---
|
|
117
|
+
# --- Sinatra helpers ---
|
|
118
118
|
|
|
119
|
+
# Register GitHelpers so plugins can call resolve_pr_target, resolve_base_branch
|
|
120
|
+
# from within Sinatra route handlers
|
|
121
|
+
helpers GitHelpers
|
|
122
|
+
|
|
123
|
+
# Dashboard authentication helpers
|
|
119
124
|
helpers do
|
|
120
125
|
def authenticate_dashboard!
|
|
121
126
|
return unless DASHBOARD_TOKEN # No token configured = no auth (local-only mode)
|