brainiac-fizzy 0.0.1 → 0.0.3

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: 900a770925d9d311e242d2262ba1d55488f1f1d99b706940412c90354fbecbe7
4
- data.tar.gz: 10745520e457d223051f8b02451b6648b2fbd63b541a595da7430ef5a22a2580
3
+ metadata.gz: d848363b737703f7d3da85256a02550c97f4f45afad53daae7d0d29c035ce167
4
+ data.tar.gz: df5257f73e63964e4cbb7d33974e1057484dc497639c393069738c25111363b0
5
5
  SHA512:
6
- metadata.gz: b5da5cb4db94444d25292a4521b73229c2e0115eaa14a4f29e1ddc0de47d87cb7bf54294d16ae59615ba0691be76ebf25040895c78a460fa7492b1f12729ff23
7
- data.tar.gz: 9d16fc1d6792ddfbd2a0ba983a9bf66b4dc46cf3d2ead4bc3c77a4b6222679d5835cb30161722224cae7578a2ddd2531075090bc1829072f7c343b7bdb30abab
6
+ metadata.gz: 5e5bcd9d72ed693d4e2e4b0ce096df51238a29782a912650e1a39df9ecba62f439c5b79b6ba6b7814cd4e18ffe310d0cbe4f147f383baca3afd25ea303679362
7
+ data.tar.gz: 4bfcef1e519b2fb9738e25d2081cbbc24ba420fc9fac889d6ddbc4e4681665d914d250f150afc85263fa3761534f4617ea21311a3983907ec1bf376a6883004b
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Brainiac
6
+ module Plugins
7
+ module Fizzy
8
+ # CLI subcommands for brainiac-fizzy plugin.
9
+ #
10
+ # Invoked when a user runs `brainiac fizzy <command>`.
11
+ module Cli
12
+ BRAINIAC_DIR = ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac"))
13
+ FIZZY_CONFIG_FILE = File.join(BRAINIAC_DIR, "fizzy.json")
14
+
15
+ class << self
16
+ def run(args)
17
+ command = args.shift
18
+
19
+ case command
20
+ when "config"
21
+ cmd_config
22
+ when "status"
23
+ cmd_status
24
+ when "setup"
25
+ cmd_setup
26
+ else
27
+ print_help
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def cmd_config
34
+ if File.exist?(FIZZY_CONFIG_FILE)
35
+ puts File.read(FIZZY_CONFIG_FILE)
36
+ else
37
+ puts "No Fizzy config found at #{FIZZY_CONFIG_FILE}"
38
+ puts "Run 'brainiac fizzy setup' to get started."
39
+ end
40
+ end
41
+
42
+ def cmd_status
43
+ server_url = detect_server_url
44
+ begin
45
+ uri = URI("#{server_url}/api/fizzy")
46
+ response = Net::HTTP.get_response(uri)
47
+ data = JSON.parse(response.body)
48
+ puts "Fizzy: #{data["enabled"] ? "enabled" : "disabled"}"
49
+ puts "Boards: #{(data["boards"] || []).join(", ")}" if data["boards"]
50
+ puts "Authorized users: #{data["authorized_users"]}" if data["authorized_users"]
51
+ rescue StandardError => e
52
+ puts "Could not reach server at #{server_url}: #{e.message}"
53
+ puts "Is the server running? Check with: brainiac status"
54
+ end
55
+ end
56
+
57
+ def cmd_setup
58
+ puts "Fizzy Setup"
59
+ puts "==========="
60
+ puts ""
61
+
62
+ if File.exist?(FIZZY_CONFIG_FILE)
63
+ config = JSON.parse(File.read(FIZZY_CONFIG_FILE))
64
+ boards = config["boards"] || {}
65
+ users = config["authorized_users"] || []
66
+
67
+ if boards.any?
68
+ puts "✓ #{boards.size} board(s) configured: #{boards.keys.join(", ")}"
69
+ else
70
+ puts "⚠ No boards configured."
71
+ puts " Edit #{FIZZY_CONFIG_FILE} to add board config."
72
+ end
73
+ puts ""
74
+
75
+ if users.any?
76
+ puts "✓ #{users.size} authorized user(s)"
77
+ else
78
+ puts "⚠ No authorized users configured."
79
+ end
80
+ else
81
+ puts "⚠ No fizzy.json found."
82
+ puts " Create #{FIZZY_CONFIG_FILE} with your board config."
83
+ puts ""
84
+ puts " Minimum config:"
85
+ puts " {"
86
+ puts ' "authorized_users": [{ "id": "user-id", "name": "You", "human": true }],'
87
+ puts ' "boards": {'
88
+ puts ' "development": {'
89
+ puts ' "board_id": "your-board-id",'
90
+ puts ' "webhook_secret": "your-secret",'
91
+ puts ' "columns": { "right_now": "col-id", "needs_review": "col-id" }'
92
+ puts " }"
93
+ puts " }"
94
+ puts " }"
95
+ end
96
+ puts ""
97
+ puts "Webhook URL: https://<your-ngrok>.ngrok-free.app/fizzy/<board-key>"
98
+ end
99
+
100
+ def print_help
101
+ puts <<~HELP
102
+ Usage: brainiac fizzy <command>
103
+
104
+ Commands:
105
+ config Show Fizzy config
106
+ status Check Fizzy status via server API
107
+ setup Show setup guide
108
+
109
+ Fizzy handles card assignment, comments, @mentions, cross-agent reviews,
110
+ duplicate detection, and planning mode via webhooks.
111
+
112
+ Webhook URL: https://<your-ngrok>/fizzy/<board-key>
113
+ Config file: #{FIZZY_CONFIG_FILE}
114
+ HELP
115
+ end
116
+
117
+ def detect_server_url
118
+ config_file = File.join(BRAINIAC_DIR, "brainiac.json")
119
+ if File.exist?(config_file)
120
+ config = JSON.parse(File.read(config_file))
121
+ config["server_url"] || "http://localhost:4567"
122
+ else
123
+ "http://localhost:4567"
124
+ end
125
+ rescue JSON::ParserError
126
+ "http://localhost:4567"
127
+ end
128
+ end
129
+ end
130
+
131
+ # Plugin CLI entry point — called by brainiac core's plugin delegation.
132
+ def self.cli(args)
133
+ Cli.run(args)
134
+ end
135
+
136
+ # Subcommand names for bash completion.
137
+ def self.completions
138
+ %w[config status setup]
139
+ end
140
+ end
141
+ end
142
+ end
@@ -89,7 +89,7 @@ module Brainiac
89
89
  tag_names = tags.map { |t| t.is_a?(Hash) ? t["name"] : t.to_s }.map(&:downcase)
90
90
 
91
91
  PROJECTS.each do |key, config|
92
- project_tags = (config["fizzy_tags"] || []).map(&:downcase)
92
+ project_tags = (config["tags"] || config["fizzy_tags"] || []).map(&:downcase)
93
93
  return [key, config] if tag_names.intersect?(project_tags)
94
94
  end
95
95
 
@@ -41,6 +41,11 @@ def scrub_invalid_attachments!(dir)
41
41
  Brainiac::Plugins::Fizzy::Helpers.scrub_invalid_attachments!(dir)
42
42
  end
43
43
 
44
+ def detect_planning_mode(text:, tags:, card_internal_id:, card_number:)
45
+ Brainiac::Plugins::Fizzy::Helpers.detect_planning_mode(text: text, tags: tags,
46
+ card_internal_id: card_internal_id, card_number: card_number)
47
+ end
48
+
44
49
  def verify_fizzy_signature!(request, payload_body, board_key: nil)
45
50
  Brainiac::Plugins::Fizzy::Helpers.verify_signature!(request, payload_body, board_key: board_key)
46
51
  end
@@ -83,6 +88,30 @@ def human_mentioned?(user_id)
83
88
  Brainiac::Plugins::Fizzy::Config.human_mentioned?(user_id)
84
89
  end
85
90
 
91
+ # Extracts user IDs from Fizzy mention markup in plain text.
92
+ # Fizzy represents mentions as @[Display Name](user-id) in plain text.
93
+ def detect_mentioned_user_ids(text)
94
+ return [] unless text
95
+
96
+ text.scan(/@\[[^\]]*\]\(([^)]+)\)/).flatten
97
+ end
98
+
99
+ # Webhook dispatch — routes incoming actions to the appropriate handler.
100
+ # Called from within the Sinatra route block, so it must be a top-level method.
101
+ def dispatch_webhook_action(action, payload)
102
+ case action
103
+ when "card_assigned"
104
+ handle_card_assigned(payload)
105
+ when "comment_created"
106
+ handle_comment(payload)
107
+ when "card_published", "card_triaged"
108
+ Brainiac::Plugins::Fizzy.handle_publish_or_triage(action, payload)
109
+ else
110
+ LOG.info "[Fizzy] Ignoring unknown action: #{action}"
111
+ [200, { status: "ignored", action: action }.to_json]
112
+ end
113
+ end
114
+
86
115
  # Top-level prompt constants — handler files reference these directly
87
116
  PROMPT_CARD_ASSIGNED = Brainiac::Plugins::Fizzy::Prompts::CARD_ASSIGNED
88
117
  PROMPT_FOLLOWUP_WORKTREE = Brainiac::Plugins::Fizzy::Prompts::FOLLOWUP_WORKTREE
@@ -67,6 +67,24 @@ end
67
67
 
68
68
  def react_to_assignment(card_number, repo_path, agent_name)
69
69
  Thread.new do
70
+ # Delete existing reactions from this agent before re-reacting
71
+ result = run_cmd("fizzy", "reaction", "list", "--card", card_number.to_s,
72
+ "--jq", "[.data[] | {id, reacter_id: .reacter.id}]",
73
+ chdir: repo_path, env: fizzy_env_for(agent_name))
74
+ reactions = JSON.parse(result)
75
+
76
+ identity = run_cmd("fizzy", "identity", "show", "--jq", ".data.accounts[0].user.id",
77
+ chdir: repo_path, env: fizzy_env_for(agent_name))
78
+ current_user_id = identity.strip.tr('"', "")
79
+
80
+ reactions.each do |reaction|
81
+ if reaction["reacter_id"] == current_user_id
82
+ run_cmd("fizzy", "reaction", "delete", reaction["id"], "--card", card_number.to_s,
83
+ chdir: repo_path, env: fizzy_env_for(agent_name))
84
+ end
85
+ end
86
+
87
+ # Add fresh reaction
70
88
  run_cmd("fizzy", "reaction", "create", "--card", card_number.to_s,
71
89
  "--content", "👍", chdir: repo_path, env: fizzy_env_for(agent_name))
72
90
  rescue StandardError => e
@@ -139,6 +139,16 @@ module Brainiac
139
139
  end
140
140
  end
141
141
 
142
+ # Determines whether a card should run in planning mode.
143
+ # Returns nil if planning is not active, or { card_id: <id> } if it is.
144
+ # Planning mode is triggered by a "plan" or "planning" tag on the card.
145
+ def detect_planning_mode(text:, tags:, card_internal_id:, card_number:)
146
+ tag_names = (tags || []).map { |t| t.is_a?(Hash) ? t["name"] : t.to_s }.map(&:downcase)
147
+ return nil unless tag_names.include?("plan") || tag_names.include?("planning")
148
+
149
+ { card_id: card_number || card_internal_id }
150
+ end
151
+
142
152
  private
143
153
 
144
154
  def detect_branch_from_comment(body, card_number)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Lightweight metadata for brainiac-fizzy.
4
+ # Loaded by `brainiac help` without pulling in the full plugin runtime.
5
+
6
+ require_relative "version"
7
+
8
+ module Brainiac
9
+ module Plugins
10
+ module Fizzy
11
+ # Returns true if Fizzy has at least one board configured.
12
+ def self.configured?
13
+ config_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "fizzy.json")
14
+ return false unless File.exist?(config_file)
15
+
16
+ config = JSON.parse(File.read(config_file))
17
+ !(config["boards"] || {}).empty?
18
+ rescue StandardError
19
+ false
20
+ end
21
+
22
+ # Help text shown in `brainiac help` when the plugin is installed.
23
+ def self.help_text
24
+ " brainiac fizzy <command> Manage Fizzy boards"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -3,7 +3,7 @@
3
3
  module Brainiac
4
4
  module Plugins
5
5
  module Fizzy
6
- VERSION = "0.0.1"
6
+ VERSION = "0.0.3"
7
7
  end
8
8
  end
9
9
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "fizzy/version"
4
+ require_relative "fizzy/metadata"
4
5
  require_relative "fizzy/config"
5
6
  require_relative "fizzy/helpers"
6
7
  require_relative "fizzy/prompts"
@@ -171,20 +172,6 @@ module Brainiac
171
172
  end
172
173
  end
173
174
 
174
- def dispatch_webhook_action(action, payload)
175
- case action
176
- when "card_assigned"
177
- handle_card_assigned(payload)
178
- when "comment_created"
179
- handle_comment(payload)
180
- when "card_published", "card_triaged"
181
- Brainiac::Plugins::Fizzy.handle_publish_or_triage(action, payload)
182
- else
183
- LOG.info "[Fizzy] Ignoring unknown action: #{action}"
184
- [200, { status: "ignored", action: action }.to_json]
185
- end
186
- end
187
-
188
175
  public
189
176
 
190
177
  def handle_publish_or_triage(action, payload)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brainiac-fizzy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis
@@ -89,6 +89,7 @@ extra_rdoc_files: []
89
89
  files:
90
90
  - README.md
91
91
  - lib/brainiac/plugins/fizzy.rb
92
+ - lib/brainiac/plugins/fizzy/cli.rb
92
93
  - lib/brainiac/plugins/fizzy/config.rb
93
94
  - lib/brainiac/plugins/fizzy/delegators.rb
94
95
  - lib/brainiac/plugins/fizzy/handlers/assignment.rb
@@ -99,6 +100,7 @@ files:
99
100
  - lib/brainiac/plugins/fizzy/handlers/deployments.rb
100
101
  - lib/brainiac/plugins/fizzy/helpers.rb
101
102
  - lib/brainiac/plugins/fizzy/hooks.rb
103
+ - lib/brainiac/plugins/fizzy/metadata.rb
102
104
  - lib/brainiac/plugins/fizzy/planning.rb
103
105
  - lib/brainiac/plugins/fizzy/prompts.rb
104
106
  - lib/brainiac/plugins/fizzy/version.rb