brainiac-fizzy 0.0.1
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 +7 -0
- data/README.md +111 -0
- data/lib/brainiac/plugins/fizzy/config.rb +104 -0
- data/lib/brainiac/plugins/fizzy/delegators.rb +106 -0
- data/lib/brainiac/plugins/fizzy/handlers/assignment.rb +128 -0
- data/lib/brainiac/plugins/fizzy/handlers/card_index.rb +389 -0
- data/lib/brainiac/plugins/fizzy/handlers/comments.rb +749 -0
- data/lib/brainiac/plugins/fizzy/handlers/dedup.rb +74 -0
- data/lib/brainiac/plugins/fizzy/handlers/deploy.rb +152 -0
- data/lib/brainiac/plugins/fizzy/handlers/deployments.rb +260 -0
- data/lib/brainiac/plugins/fizzy/helpers.rb +165 -0
- data/lib/brainiac/plugins/fizzy/hooks.rb +371 -0
- data/lib/brainiac/plugins/fizzy/planning.rb +73 -0
- data/lib/brainiac/plugins/fizzy/prompts.rb +119 -0
- data/lib/brainiac/plugins/fizzy/version.rb +9 -0
- data/lib/brainiac/plugins/fizzy.rb +212 -0
- data/lib/brainiac_fizzy.rb +4 -0
- metadata +128 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "fizzy/version"
|
|
4
|
+
require_relative "fizzy/config"
|
|
5
|
+
require_relative "fizzy/helpers"
|
|
6
|
+
require_relative "fizzy/prompts"
|
|
7
|
+
require_relative "fizzy/planning"
|
|
8
|
+
require_relative "fizzy/hooks"
|
|
9
|
+
require_relative "fizzy/delegators"
|
|
10
|
+
|
|
11
|
+
# Handler sub-modules (define top-level functions for webhook handling)
|
|
12
|
+
require_relative "fizzy/handlers/assignment"
|
|
13
|
+
require_relative "fizzy/handlers/comments"
|
|
14
|
+
require_relative "fizzy/handlers/dedup"
|
|
15
|
+
require_relative "fizzy/handlers/deploy"
|
|
16
|
+
require_relative "fizzy/handlers/card_index"
|
|
17
|
+
require_relative "fizzy/handlers/deployments"
|
|
18
|
+
|
|
19
|
+
module Brainiac
|
|
20
|
+
module Plugins
|
|
21
|
+
module Fizzy
|
|
22
|
+
class << self
|
|
23
|
+
# Called by Brainiac plugin system during server startup.
|
|
24
|
+
#
|
|
25
|
+
# @param app [Sinatra::Application] The running Brainiac server
|
|
26
|
+
def register(app)
|
|
27
|
+
# Load Fizzy config
|
|
28
|
+
Brainiac::Plugins::Fizzy::Config.load!
|
|
29
|
+
|
|
30
|
+
# Register channel prompt
|
|
31
|
+
Brainiac.register_channel_prompt(:fizzy,
|
|
32
|
+
Brainiac::Plugins::Fizzy::Prompts::CHANNEL,
|
|
33
|
+
pre_post_check: Brainiac::Plugins::Fizzy::Prompts::PRE_POST_CHECK)
|
|
34
|
+
|
|
35
|
+
# Register lifecycle hooks
|
|
36
|
+
Brainiac::Plugins::Fizzy::Hooks.register_all!
|
|
37
|
+
|
|
38
|
+
# Set up webhook route
|
|
39
|
+
setup_routes(app)
|
|
40
|
+
|
|
41
|
+
LOG.info "[Fizzy] Plugin registered (webhook: /fizzy)"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def setup_routes(app)
|
|
47
|
+
setup_webhook_route(app)
|
|
48
|
+
setup_api_routes(app)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def setup_webhook_route(app)
|
|
52
|
+
app.post "/fizzy/?:board_key?" do
|
|
53
|
+
content_type :json
|
|
54
|
+
request.body.rewind
|
|
55
|
+
payload_body = request.body.read
|
|
56
|
+
board_key = params["board_key"]
|
|
57
|
+
|
|
58
|
+
LOG.debug "[Fizzy] Webhook received: board_key=#{board_key}, content_length=#{payload_body.length}" if LOG.debug?
|
|
59
|
+
|
|
60
|
+
unless Brainiac::Plugins::Fizzy::Helpers.verify_signature!(request, payload_body, board_key: board_key)
|
|
61
|
+
LOG.warn "[Fizzy] Signature verification failed for board_key=#{board_key}"
|
|
62
|
+
halt 401, { error: "Invalid signature" }.to_json
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
payload = JSON.parse(payload_body)
|
|
66
|
+
event_id = payload["id"]
|
|
67
|
+
action = payload["action"]
|
|
68
|
+
|
|
69
|
+
LOG.info "[Fizzy] Received event #{event_id}: action=#{action}"
|
|
70
|
+
|
|
71
|
+
if already_processed?(event_id)
|
|
72
|
+
LOG.info "[Fizzy] Skipping duplicate event #{event_id}"
|
|
73
|
+
halt 200, { status: "duplicate" }.to_json
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
reload_projects!
|
|
77
|
+
reload_agent_registry!
|
|
78
|
+
|
|
79
|
+
status_code, body = dispatch_webhook_action(action, payload)
|
|
80
|
+
LOG.info "[Fizzy] #{action} response: #{status_code} - #{body}"
|
|
81
|
+
halt status_code, body
|
|
82
|
+
rescue JSON::ParserError => e
|
|
83
|
+
LOG.error "[Fizzy] Invalid JSON: #{e.message}"
|
|
84
|
+
halt 400, { error: "Invalid JSON" }.to_json
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
LOG.error "[Fizzy] Unhandled error: #{e.message}\n#{e.backtrace.first(5).join("\n")}"
|
|
87
|
+
halt 500, { error: e.message }.to_json
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def setup_api_routes(app)
|
|
92
|
+
# API status endpoint
|
|
93
|
+
app.get "/api/fizzy" do
|
|
94
|
+
content_type :json
|
|
95
|
+
config = Brainiac::Plugins::Fizzy::Config.current
|
|
96
|
+
{
|
|
97
|
+
enabled: true,
|
|
98
|
+
boards: config["boards"]&.keys || [],
|
|
99
|
+
authorized_users: (config["authorized_users"] || []).size
|
|
100
|
+
}.to_json
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Card index API (duplicate detection)
|
|
104
|
+
app.get "/api/card-index" do
|
|
105
|
+
content_type :json
|
|
106
|
+
halt 404, { error: "Card index not available" }.to_json unless defined?(CARD_INDEX)
|
|
107
|
+
|
|
108
|
+
query = params["q"]
|
|
109
|
+
if query && !query.empty?
|
|
110
|
+
similar = CARD_INDEX.find_similar_cards(query)
|
|
111
|
+
{ query: query, matches: similar, total_indexed: CARD_INDEX.size }.to_json
|
|
112
|
+
else
|
|
113
|
+
{ total: CARD_INDEX.size, cards: CARD_INDEX }.to_json
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Deployment API routes (if deployments are configured)
|
|
118
|
+
return unless defined?(DEPLOYMENTS_CONFIG)
|
|
119
|
+
|
|
120
|
+
app.get "/api/deployments" do
|
|
121
|
+
content_type :json
|
|
122
|
+
reload_deployments_config!
|
|
123
|
+
reload_deployment_state!
|
|
124
|
+
{ deployments: deployment_status }.to_json
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
app.post "/api/deployments/:env" do
|
|
128
|
+
content_type :json
|
|
129
|
+
env_key = params["env"]
|
|
130
|
+
request.body.rewind
|
|
131
|
+
payload = JSON.parse(request.body.read)
|
|
132
|
+
result = deploy_to_environment(env_key, worktree_path: payload["worktree"], deployed_by: payload["deployed_by"])
|
|
133
|
+
if result[:error]
|
|
134
|
+
halt 404, result.to_json
|
|
135
|
+
else
|
|
136
|
+
{ status: "deployed", env: env_key, deployment: result }.to_json
|
|
137
|
+
end
|
|
138
|
+
rescue JSON::ParserError
|
|
139
|
+
halt 400, { error: "Invalid JSON" }.to_json
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
app.delete "/api/deployments/:env" do
|
|
143
|
+
content_type :json
|
|
144
|
+
env_key = params["env"]
|
|
145
|
+
state = load_deployment_state
|
|
146
|
+
if state.key?(env_key)
|
|
147
|
+
state[env_key] = { "status" => "available", "cleared_at" => Time.now.iso8601, "last_card" => state[env_key]["card_number"] }
|
|
148
|
+
save_deployment_state(state)
|
|
149
|
+
DEPLOYMENT_STATE.replace(state)
|
|
150
|
+
LOG.info "[Fizzy:Deploy] Manually cleared #{env_key}"
|
|
151
|
+
{ status: "cleared", env: env_key }.to_json
|
|
152
|
+
else
|
|
153
|
+
halt 404, { error: "Unknown environment: #{env_key}" }.to_json
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
app.post "/api/deployments/:env/deploying" do
|
|
158
|
+
content_type :json
|
|
159
|
+
env_key = params["env"]
|
|
160
|
+
config = DEPLOYMENTS_CONFIG["environments"] || {}
|
|
161
|
+
halt 404, { error: "Unknown environment: #{env_key}" }.to_json unless config.key?(env_key)
|
|
162
|
+
request.body.rewind
|
|
163
|
+
payload = begin
|
|
164
|
+
JSON.parse(request.body.read)
|
|
165
|
+
rescue StandardError
|
|
166
|
+
{}
|
|
167
|
+
end
|
|
168
|
+
mark_deploying(env_key, worktree_path: payload["worktree"] || "")
|
|
169
|
+
LOG.info "[Fizzy:Deploy] #{env_key} marked deploying via API"
|
|
170
|
+
{ status: "deploying", env: env_key }.to_json
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
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
|
+
public
|
|
189
|
+
|
|
190
|
+
def handle_publish_or_triage(action, payload)
|
|
191
|
+
eventable = payload["eventable"] || {}
|
|
192
|
+
card_number = eventable["number"]&.to_s
|
|
193
|
+
|
|
194
|
+
if action == "card_triaged" && card_number
|
|
195
|
+
return [200, { status: "ignored", reason: "self_move" }.to_json] if self_move_recent?(card_number)
|
|
196
|
+
return [200, { status: "ignored", reason: "card_merged" }.to_json] if work_item_merged?(card_number)
|
|
197
|
+
|
|
198
|
+
card_key = "card-#{card_number}"
|
|
199
|
+
return [200, { status: "ignored", reason: "recently_completed" }.to_json] if recently_completed?(card_key)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
if action == "card_published"
|
|
203
|
+
assignees = eventable["assignees"] || []
|
|
204
|
+
return handle_card_assigned(payload) if assignees.any? { |a| local_agent_names.include?(a["name"]) }
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
handle_card_published(payload)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: brainiac-fizzy
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Andy Davis
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: brainiac
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.0.8
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.0.8
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: minitest
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.25'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '5.25'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rake
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '13.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '13.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rubocop
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.75'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.75'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rubocop-performance
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '1.25'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '1.25'
|
|
82
|
+
description: Full Fizzy integration for Brainiac — card assignment, comment routing,
|
|
83
|
+
@mentions, cross-agent reviews, duplicate detection, deploy shortcuts, deployment
|
|
84
|
+
tracking, and planning mode. Uses Brainiac's hook system for lifecycle integration
|
|
85
|
+
(PR merge → card close, agent complete → column move).
|
|
86
|
+
executables: []
|
|
87
|
+
extensions: []
|
|
88
|
+
extra_rdoc_files: []
|
|
89
|
+
files:
|
|
90
|
+
- README.md
|
|
91
|
+
- lib/brainiac/plugins/fizzy.rb
|
|
92
|
+
- lib/brainiac/plugins/fizzy/config.rb
|
|
93
|
+
- lib/brainiac/plugins/fizzy/delegators.rb
|
|
94
|
+
- lib/brainiac/plugins/fizzy/handlers/assignment.rb
|
|
95
|
+
- lib/brainiac/plugins/fizzy/handlers/card_index.rb
|
|
96
|
+
- lib/brainiac/plugins/fizzy/handlers/comments.rb
|
|
97
|
+
- lib/brainiac/plugins/fizzy/handlers/dedup.rb
|
|
98
|
+
- lib/brainiac/plugins/fizzy/handlers/deploy.rb
|
|
99
|
+
- lib/brainiac/plugins/fizzy/handlers/deployments.rb
|
|
100
|
+
- lib/brainiac/plugins/fizzy/helpers.rb
|
|
101
|
+
- lib/brainiac/plugins/fizzy/hooks.rb
|
|
102
|
+
- lib/brainiac/plugins/fizzy/planning.rb
|
|
103
|
+
- lib/brainiac/plugins/fizzy/prompts.rb
|
|
104
|
+
- lib/brainiac/plugins/fizzy/version.rb
|
|
105
|
+
- lib/brainiac_fizzy.rb
|
|
106
|
+
homepage: https://github.com/stowzilla/brainiac-fizzy
|
|
107
|
+
licenses:
|
|
108
|
+
- MIT
|
|
109
|
+
metadata:
|
|
110
|
+
rubygems_mfa_required: 'true'
|
|
111
|
+
rdoc_options: []
|
|
112
|
+
require_paths:
|
|
113
|
+
- lib
|
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - ">="
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '3.4'
|
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
requirements: []
|
|
125
|
+
rubygems_version: 3.6.9
|
|
126
|
+
specification_version: 4
|
|
127
|
+
summary: Fizzy card management plugin for Brainiac
|
|
128
|
+
test_files: []
|