Module: Brainiac::Plugins::Fizzy

Defined in:
lib/brainiac/plugins/fizzy.rb,
lib/brainiac/plugins/fizzy/cli.rb,
lib/brainiac/plugins/fizzy/hooks.rb,
lib/brainiac/plugins/fizzy/config.rb,
lib/brainiac/plugins/fizzy/helpers.rb,
lib/brainiac/plugins/fizzy/prompts.rb,
lib/brainiac/plugins/fizzy/version.rb,
lib/brainiac/plugins/fizzy/metadata.rb,
lib/brainiac/plugins/fizzy/planning.rb,
lib/brainiac/plugins/fizzy/handlers/summarize.rb

Defined Under Namespace

Modules: Cli, Config, Helpers, Hooks, Planning, Prompts, Summarize

Constant Summary collapse

VERSION =
"0.0.29"

Class Method Summary collapse

Class Method Details

.board_assign_completions(args) ⇒ Object

Completions for brainiac fizzy board assign <project> <board>



691
692
693
694
695
696
697
698
699
700
# File 'lib/brainiac/plugins/fizzy/cli.rb', line 691

def self.board_assign_completions(args)
  case args.length
  when 0
    load_project_keys
  when 1
    load_board_keys
  else
    []
  end
end

.board_completions(args) ⇒ Object

Nested completions for brainiac fizzy board ...



676
677
678
679
680
681
682
683
684
685
686
687
688
# File 'lib/brainiac/plugins/fizzy/cli.rb', line 676

def self.board_completions(args)
  board_subcommands = %w[assign columns list ls setup webhook]
  return board_subcommands if args.empty?

  case args[0]
  when "assign"
    board_assign_completions(args[1..])
  when "columns", "webhook"
    board_key_completions(args[1..])
  else
    []
  end
end

.board_key_completions(args) ⇒ Object

Completions for commands that take a board key as next arg



703
704
705
706
707
# File 'lib/brainiac/plugins/fizzy/cli.rb', line 703

def self.board_key_completions(args)
  return load_board_keys if args.empty?

  []
end

.cli(args) ⇒ Object

Plugin CLI entry point — called by brainiac core's plugin delegation.



656
657
658
# File 'lib/brainiac/plugins/fizzy/cli.rb', line 656

def self.cli(args)
  Cli.run(args)
end

.completions(args = []) ⇒ Object

Subcommand names for bash completion. When called with no args, returns top-level subcommands. When called with args (the words typed so far after the plugin name), returns context-sensitive completions for nested subcommands.



664
665
666
667
668
669
670
671
672
673
# File 'lib/brainiac/plugins/fizzy/cli.rb', line 664

def self.completions(args = [])
  return %w[board config setup status] if args.empty?

  case args[0]
  when "board"
    board_completions(args[1..])
  else
    []
  end
end

.configured?Boolean

Returns true if Fizzy has at least one board configured.

Returns:

  • (Boolean)


12
13
14
15
16
17
18
19
20
# File 'lib/brainiac/plugins/fizzy/metadata.rb', line 12

def self.configured?
  config_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "fizzy.json")
  return false unless File.exist?(config_file)

  config = JSON.parse(File.read(config_file))
  !(config["boards"] || {}).empty?
rescue StandardError
  false
end

.handle_publish_or_triage(action, payload, board_key: nil) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/brainiac/plugins/fizzy.rb', line 199

def handle_publish_or_triage(action, payload, board_key: nil)
  eventable = payload["eventable"] || {}
  card_number = eventable["number"]&.to_s

  if action == "card_triaged" && card_number
    return [200, { status: "ignored", reason: "self_move" }.to_json] if self_move_recent?(card_number)
    return [200, { status: "ignored", reason: "card_merged" }.to_json] if work_item_merged?(card_number)

    card_key = "card-#{card_number}"
    return [200, { status: "ignored", reason: "recently_completed" }.to_json] if recently_completed?(card_key)
  end

  if action == "card_published"
    assignees = eventable["assignees"] || []
    return handle_card_assigned(payload, board_key: board_key) if assignees.any? { |a| local_agent_names.include?(a["name"]) }
  end

  handle_card_published(payload, board_key: board_key)
end

.help_textObject

Help text shown in brainiac help when the plugin is installed.



23
24
25
# File 'lib/brainiac/plugins/fizzy/metadata.rb', line 23

def self.help_text
  "    brainiac fizzy <command>      Manage Fizzy boards"
end

.load_board_keysObject

Load board keys from fizzy.json for completion



710
711
712
713
714
715
716
717
718
# File 'lib/brainiac/plugins/fizzy/cli.rb', line 710

def self.load_board_keys
  config_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "fizzy.json")
  return [] unless File.exist?(config_file)

  config = JSON.parse(File.read(config_file))
  (config["boards"] || {}).keys
rescue StandardError
  []
end

.load_project_keysObject

Load project keys from projects.json for completion



721
722
723
724
725
726
727
728
# File 'lib/brainiac/plugins/fizzy/cli.rb', line 721

def self.load_project_keys
  projects_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "projects.json")
  return [] unless File.exist?(projects_file)

  JSON.parse(File.read(projects_file)).keys
rescue StandardError
  []
end

.on_agent_created(agent_key, entry) ⇒ Object

Called by brainiac CLI after agent create — prompts for Fizzy user ID.



731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
# File 'lib/brainiac/plugins/fizzy/cli.rb', line 731

def self.on_agent_created(agent_key, entry)
  return unless $stdin.tty?

  config_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "fizzy.json")
  return unless File.exist?(config_file)

  display_name = entry["display_name"] || agent_key.capitalize
  puts ""
  puts "  [Fizzy] Configure Fizzy for #{display_name}?"
  print "  Fizzy user ID (or blank to skip): "
  user_id = $stdin.gets&.chomp
  return if user_id.nil? || user_id.empty?

  config = JSON.parse(File.read(config_file))
  config["authorized_users"] ||= []

  existing = config["authorized_users"].find { |u| u["id"] == user_id || u["name"]&.downcase == display_name.downcase }
  if existing
    puts "  ✓ #{display_name} already in authorized_users (id: #{existing["id"]})"
    return
  end

  config["authorized_users"] << { "id" => user_id, "name" => display_name, "human" => false }
  File.write(config_file, JSON.pretty_generate(config))
  puts "  ✓ Added #{display_name} (#{user_id}) to fizzy.json authorized_users"
rescue JSON::ParserError => e
  puts "  ⚠ Could not update fizzy.json: #{e.message}"
end

.on_agent_removed(agent_key, display_name) ⇒ Object

Called by brainiac CLI after agent remove — removes from Fizzy authorized_users.



761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
# File 'lib/brainiac/plugins/fizzy/cli.rb', line 761

def self.on_agent_removed(agent_key, display_name)
  config_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "fizzy.json")
  return unless File.exist?(config_file)

  config = JSON.parse(File.read(config_file))
  users = config["authorized_users"]
  return unless users

  original_size = users.size
  users.reject! { |u| u["name"]&.downcase == display_name.downcase || u["name"]&.downcase == agent_key }
  return if users.size == original_size

  File.write(config_file, JSON.pretty_generate(config))
  puts "  [Fizzy] Removed #{display_name} from fizzy.json authorized_users"
rescue JSON::ParserError
  nil
end

.register(app) ⇒ Object

Called by Brainiac plugin system during server startup.

Parameters:

  • app (Sinatra::Application)

    The running Brainiac server



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/brainiac/plugins/fizzy.rb', line 28

def register(app)
  # Load Fizzy config
  Brainiac::Plugins::Fizzy::Config.load!

  # Register channel prompt
  Brainiac.register_channel_prompt(:fizzy,
                                   Brainiac::Plugins::Fizzy::Prompts::CHANNEL,
                                   pre_post_check: Brainiac::Plugins::Fizzy::Prompts::PRE_POST_CHECK)

  # Register lifecycle hooks
  Brainiac::Plugins::Fizzy::Hooks.register_all!

  # Set up webhook route
  setup_routes(app)

  LOG.info "[Fizzy] Plugin registered (webhook: /fizzy)"
end