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
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 900a770925d9d311e242d2262ba1d55488f1f1d99b706940412c90354fbecbe7
|
|
4
|
+
data.tar.gz: 10745520e457d223051f8b02451b6648b2fbd63b541a595da7430ef5a22a2580
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b5da5cb4db94444d25292a4521b73229c2e0115eaa14a4f29e1ddc0de47d87cb7bf54294d16ae59615ba0691be76ebf25040895c78a460fa7492b1f12729ff23
|
|
7
|
+
data.tar.gz: 9d16fc1d6792ddfbd2a0ba983a9bf66b4dc46cf3d2ead4bc3c77a4b6222679d5835cb30161722224cae7578a2ddd2531075090bc1829072f7c343b7bdb30abab
|
data/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# brainiac-fizzy
|
|
2
|
+
|
|
3
|
+
Fizzy card management plugin for [Brainiac](https://github.com/stowzilla/brainiac).
|
|
4
|
+
|
|
5
|
+
Handles the full Fizzy integration: card assignment, comment routing, @mentions, cross-agent reviews, duplicate detection, deploy shortcuts, and deployment environment tracking.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
brainiac install fizzy
|
|
11
|
+
brainiac restart
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or manually:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
gem install brainiac-fizzy
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then add to `~/.brainiac/plugins.json`:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"plugins": [
|
|
25
|
+
{ "name": "fizzy", "gem": "brainiac-fizzy" }
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Configuration
|
|
31
|
+
|
|
32
|
+
Fizzy configuration lives in `~/.brainiac/fizzy.json` (same as before):
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"authorized_users": [
|
|
37
|
+
{ "id": "user-id-1", "name": "Andy", "human": true },
|
|
38
|
+
{ "id": "agent-id-1", "name": "Galen", "human": false }
|
|
39
|
+
],
|
|
40
|
+
"boards": {
|
|
41
|
+
"development": {
|
|
42
|
+
"board_id": "your-board-id",
|
|
43
|
+
"webhook_secret": "secret-for-this-board",
|
|
44
|
+
"columns": {
|
|
45
|
+
"right_now": "column-id",
|
|
46
|
+
"needs_review": "column-id",
|
|
47
|
+
"uat": "column-id"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What This Plugin Handles
|
|
55
|
+
|
|
56
|
+
| Event | Action |
|
|
57
|
+
|-------|--------|
|
|
58
|
+
| Card assigned | Creates worktree, maps card to branch, dispatches assigned agent |
|
|
59
|
+
| Card published | Duplicate detection (trigram + semantic) |
|
|
60
|
+
| @mention in comment | Routes to mentioned agent (cross-agent reviews) |
|
|
61
|
+
| Follow-up comment | Runs card's assigned agent in existing worktree |
|
|
62
|
+
| Deploy shortcut | Clones branch to deployment environment (dev01, dev02) |
|
|
63
|
+
|
|
64
|
+
## Webhook Setup
|
|
65
|
+
|
|
66
|
+
Set your Fizzy webhook URL to:
|
|
67
|
+
```
|
|
68
|
+
https://your-ngrok.ngrok-free.app/fizzy/development
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Where `development` is the board key from `fizzy.json`. Set the secret to the board's `webhook_secret`.
|
|
72
|
+
|
|
73
|
+
## Dependencies on Brainiac Core
|
|
74
|
+
|
|
75
|
+
This plugin runs inside the brainiac server process and uses core functions:
|
|
76
|
+
|
|
77
|
+
- `verify_signature!` — webhook HMAC verification
|
|
78
|
+
- `run_agent` — agent CLI dispatch
|
|
79
|
+
- `session_active?`, `already_processed?` — deduplication
|
|
80
|
+
- `identify_project_by_tags` — card-to-project mapping
|
|
81
|
+
- `create_or_reuse_worktree` — git worktree management
|
|
82
|
+
- `prefetch_card_context` — card body/comments pre-fetch
|
|
83
|
+
- `render_prompt` — prompt template composition
|
|
84
|
+
- `reload_projects!`, `reload_agent_registry!` — config hot-reload
|
|
85
|
+
|
|
86
|
+
These are all provided by the `brainiac` gem (runtime dependency).
|
|
87
|
+
|
|
88
|
+
## Migrating from Built-in Handler
|
|
89
|
+
|
|
90
|
+
If upgrading from brainiac's built-in Fizzy handler:
|
|
91
|
+
|
|
92
|
+
1. Install the plugin: `brainiac install fizzy`
|
|
93
|
+
2. Disable the built-in handler in `~/.brainiac/brainiac.json`:
|
|
94
|
+
```json
|
|
95
|
+
{ "handlers": { "fizzy": false } }
|
|
96
|
+
```
|
|
97
|
+
3. Restart: `brainiac restart`
|
|
98
|
+
|
|
99
|
+
The plugin uses the exact same config files and functions — it's a drop-in replacement.
|
|
100
|
+
|
|
101
|
+
## Development
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
cd ~/Code/brainiac-fizzy
|
|
105
|
+
bundle install
|
|
106
|
+
rake test
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Brainiac
|
|
4
|
+
module Plugins
|
|
5
|
+
module Fizzy
|
|
6
|
+
# Fizzy configuration — loads ~/.brainiac/fizzy.json.
|
|
7
|
+
# Provides board config, webhook secrets, authorized users, and column IDs.
|
|
8
|
+
module Config
|
|
9
|
+
FIZZY_CONFIG_FILE = File.join(
|
|
10
|
+
ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")),
|
|
11
|
+
"fizzy.json"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
@config = {}
|
|
15
|
+
@boards = {}
|
|
16
|
+
@authorized_user_ids = []
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
attr_reader :config, :boards, :authorized_user_ids
|
|
20
|
+
|
|
21
|
+
def load!
|
|
22
|
+
@config = if File.exist?(FIZZY_CONFIG_FILE)
|
|
23
|
+
JSON.parse(File.read(FIZZY_CONFIG_FILE))
|
|
24
|
+
else
|
|
25
|
+
{}
|
|
26
|
+
end
|
|
27
|
+
@boards = @config["boards"] || {}
|
|
28
|
+
@authorized_user_ids = (@config["authorized_users"] || []).map { |u| u["id"] }
|
|
29
|
+
rescue JSON::ParserError => e
|
|
30
|
+
LOG.error "[Fizzy] Failed to parse fizzy.json: #{e.message}" if defined?(LOG)
|
|
31
|
+
@config = {}
|
|
32
|
+
@boards = {}
|
|
33
|
+
@authorized_user_ids = []
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reload!
|
|
37
|
+
load!
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def current
|
|
41
|
+
@config
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def board_config(board_key)
|
|
45
|
+
@boards[board_key.to_s]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def board_webhook_secret(board_key)
|
|
49
|
+
config = board_config(board_key)
|
|
50
|
+
config&.dig("webhook_secret") || ENV.fetch("FIZZY_WEBHOOK_SECRET", nil)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def board_column_id(board_key, column_name)
|
|
54
|
+
config = board_config(board_key)
|
|
55
|
+
config&.dig("columns", column_name.to_s)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def board_key_for_id(board_id)
|
|
59
|
+
@boards.each do |key, config|
|
|
60
|
+
return key if config["board_id"] == board_id
|
|
61
|
+
end
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def board_key_for_project(project_config)
|
|
66
|
+
fizzy_yaml = File.join(project_config["repo_path"], ".fizzy.yaml")
|
|
67
|
+
return nil unless File.exist?(fizzy_yaml)
|
|
68
|
+
|
|
69
|
+
require "yaml"
|
|
70
|
+
data = YAML.safe_load_file(fizzy_yaml)
|
|
71
|
+
board_id = data["board"]
|
|
72
|
+
board_key_for_id(board_id)
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
LOG.warn "[Fizzy] Could not read .fizzy.yaml: #{e.message}" if defined?(LOG)
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def authorized?(payload)
|
|
79
|
+
creator_id = payload.dig("creator", "id")
|
|
80
|
+
@authorized_user_ids.include?(creator_id)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def human_mentioned?(user_id)
|
|
84
|
+
user = (@config["authorized_users"] || []).find { |u| u["id"] == user_id }
|
|
85
|
+
user && user["human"]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def identify_project_by_tags(tags)
|
|
89
|
+
tag_names = tags.map { |t| t.is_a?(Hash) ? t["name"] : t.to_s }.map(&:downcase)
|
|
90
|
+
|
|
91
|
+
PROJECTS.each do |key, config|
|
|
92
|
+
project_tags = (config["fizzy_tags"] || []).map(&:downcase)
|
|
93
|
+
return [key, config] if tag_names.intersect?(project_tags)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Fall back to default project
|
|
97
|
+
default_key = default_project_key
|
|
98
|
+
default_key ? [default_key, PROJECTS[default_key]] : nil
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Top-level convenience methods that delegate to Fizzy plugin modules.
|
|
4
|
+
#
|
|
5
|
+
# The handler files (assignment.rb, comments.rb, etc.) were originally
|
|
6
|
+
# top-level functions in brainiac core. They call helpers like
|
|
7
|
+
# `fizzy_env_for`, `identify_project_by_tags`, etc. as top-level methods.
|
|
8
|
+
#
|
|
9
|
+
# These delegators make them available at top level so the handler files
|
|
10
|
+
# work without modification.
|
|
11
|
+
|
|
12
|
+
def fizzy_token_for(agent_name)
|
|
13
|
+
Brainiac::Plugins::Fizzy::Helpers.fizzy_token_for(agent_name)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def fizzy_env_for(agent_name)
|
|
17
|
+
Brainiac::Plugins::Fizzy::Helpers.fizzy_env_for(agent_name)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def default_fizzy_env
|
|
21
|
+
Brainiac::Plugins::Fizzy::Helpers.default_fizzy_env
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def prefetch_card_context(card_number, repo_path:, agent_name: nil)
|
|
25
|
+
Brainiac::Plugins::Fizzy::Helpers.prefetch_card_context(card_number, repo_path: repo_path, agent_name: agent_name)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def move_card_to_column(card_number, column_name, project_config:, agent_name: nil)
|
|
29
|
+
Brainiac::Plugins::Fizzy::Helpers.move_card_to_column(card_number, column_name, project_config: project_config, agent_name: agent_name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def append_fizzy_comment_footer(card_number, project_config:, agent_name: nil)
|
|
33
|
+
Brainiac::Plugins::Fizzy::Helpers.append_fizzy_comment_footer(card_number, project_config: project_config, agent_name: agent_name)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def ensure_fizzy_yaml!(chdir, project_config)
|
|
37
|
+
Brainiac::Plugins::Fizzy::Helpers.ensure_fizzy_yaml!(chdir, project_config)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def scrub_invalid_attachments!(dir)
|
|
41
|
+
Brainiac::Plugins::Fizzy::Helpers.scrub_invalid_attachments!(dir)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def verify_fizzy_signature!(request, payload_body, board_key: nil)
|
|
45
|
+
Brainiac::Plugins::Fizzy::Helpers.verify_signature!(request, payload_body, board_key: board_key)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Legacy alias used by handler files
|
|
49
|
+
def verify_signature!(request, payload_body, board_key: nil)
|
|
50
|
+
Brainiac::Plugins::Fizzy::Helpers.verify_signature!(request, payload_body, board_key: board_key)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Config delegators
|
|
54
|
+
def identify_project_by_tags(tags)
|
|
55
|
+
Brainiac::Plugins::Fizzy::Config.identify_project_by_tags(tags)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def board_config(board_key)
|
|
59
|
+
Brainiac::Plugins::Fizzy::Config.board_config(board_key)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def board_webhook_secret(board_key)
|
|
63
|
+
Brainiac::Plugins::Fizzy::Config.board_webhook_secret(board_key)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def board_column_id(board_key, column_name)
|
|
67
|
+
Brainiac::Plugins::Fizzy::Config.board_column_id(board_key, column_name)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def board_key_for_project(project_config)
|
|
71
|
+
Brainiac::Plugins::Fizzy::Config.board_key_for_project(project_config)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def board_key_for_id(board_id)
|
|
75
|
+
Brainiac::Plugins::Fizzy::Config.board_key_for_id(board_id)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def authorized?(payload)
|
|
79
|
+
Brainiac::Plugins::Fizzy::Config.authorized?(payload)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def human_mentioned?(user_id)
|
|
83
|
+
Brainiac::Plugins::Fizzy::Config.human_mentioned?(user_id)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Top-level prompt constants — handler files reference these directly
|
|
87
|
+
PROMPT_CARD_ASSIGNED = Brainiac::Plugins::Fizzy::Prompts::CARD_ASSIGNED
|
|
88
|
+
PROMPT_FOLLOWUP_WORKTREE = Brainiac::Plugins::Fizzy::Prompts::FOLLOWUP_WORKTREE
|
|
89
|
+
PROMPT_FOLLOWUP_NO_WORKTREE = Brainiac::Plugins::Fizzy::Prompts::FOLLOWUP_NO_WORKTREE
|
|
90
|
+
PROMPT_MENTION = Brainiac::Plugins::Fizzy::Prompts::MENTION
|
|
91
|
+
PROMPT_CROSS_AGENT_REVIEW = Brainiac::Plugins::Fizzy::Prompts::CROSS_AGENT_REVIEW
|
|
92
|
+
|
|
93
|
+
# Config constants — handler files reference these as top-level constants.
|
|
94
|
+
# These are evaluated after Config.load! has been called (during plugin register).
|
|
95
|
+
# Use a delegating object so it always reflects current config state.
|
|
96
|
+
FIZZY_CONFIG = Class.new do
|
|
97
|
+
def fetch(key, default = nil) = Brainiac::Plugins::Fizzy::Config.current.fetch(key, default)
|
|
98
|
+
def [](key) = Brainiac::Plugins::Fizzy::Config.current[key]
|
|
99
|
+
def dig(*keys) = Brainiac::Plugins::Fizzy::Config.current.dig(*keys)
|
|
100
|
+
end.new
|
|
101
|
+
|
|
102
|
+
AUTHORIZED_USER_IDS = Class.new do
|
|
103
|
+
def include?(id) = Brainiac::Plugins::Fizzy::Config.authorized_user_ids.include?(id)
|
|
104
|
+
def map(&) = Brainiac::Plugins::Fizzy::Config.authorized_user_ids.map(&)
|
|
105
|
+
def any?(&) = Brainiac::Plugins::Fizzy::Config.authorized_user_ids.any?(&)
|
|
106
|
+
end.new
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Fizzy card assignment handler.
|
|
4
|
+
#
|
|
5
|
+
# When a card is assigned to a local agent, creates a worktree, builds the prompt,
|
|
6
|
+
# and dispatches the agent to begin work.
|
|
7
|
+
|
|
8
|
+
def handle_card_assigned(payload)
|
|
9
|
+
eventable = payload["eventable"] || {}
|
|
10
|
+
assignees = eventable["assignees"] || []
|
|
11
|
+
|
|
12
|
+
local_names = local_agent_names
|
|
13
|
+
assigned_agent = assignees.map { |a| a["name"] }.find { |name| local_names.include?(name) }
|
|
14
|
+
|
|
15
|
+
assignee_names = assignees.map { |a| a["name"] }.join(", ")
|
|
16
|
+
LOG.info "[Fizzy] Card assigned to: [#{assignee_names}], local agents: [#{local_names.join(", ")}]"
|
|
17
|
+
|
|
18
|
+
return ignore_assignment("wrong assignee", assignee_names, local_names) unless assigned_agent
|
|
19
|
+
return ignore_unauthorized(payload, eventable) unless authorized?(payload)
|
|
20
|
+
|
|
21
|
+
card_number = eventable["number"]
|
|
22
|
+
card_internal_id = eventable["id"]
|
|
23
|
+
title = eventable["title"] || "untitled"
|
|
24
|
+
tags = eventable["tags"] || []
|
|
25
|
+
|
|
26
|
+
project_result = identify_project_by_tags(tags)
|
|
27
|
+
unless project_result
|
|
28
|
+
tag_names = tags.map { |t| t.is_a?(Hash) ? t["name"] : t }.join(", ")
|
|
29
|
+
LOG.warn "No project found for card ##{card_number} with tags: #{tag_names}"
|
|
30
|
+
return [200, { status: "ignored", reason: "no matching project" }.to_json]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
project_key, project_config = project_result
|
|
34
|
+
repo_path = project_config["repo_path"]
|
|
35
|
+
branch = "fizzy-#{card_number}-#{slugify(title)}"
|
|
36
|
+
|
|
37
|
+
card_key = "card-#{card_number}"
|
|
38
|
+
if session_active?(card_key)
|
|
39
|
+
LOG.info "Skipping card ##{card_number} — agent session already active"
|
|
40
|
+
return [200, { status: "ignored", reason: "session already active" }.to_json]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
LOG.info "Card ##{card_number} assigned to #{assigned_agent} for project '#{project_key}', " \
|
|
44
|
+
"creating worktree: #{branch} (model: #{detect_model(project_config, tags: tags) || "default"})"
|
|
45
|
+
|
|
46
|
+
react_to_assignment(card_number, repo_path, assigned_agent)
|
|
47
|
+
worktree_path = setup_assigned_worktree(repo_path, branch, card_internal_id, card_number, project_key, assigned_agent)
|
|
48
|
+
|
|
49
|
+
dispatch_assigned_card(
|
|
50
|
+
card_number: card_number, card_internal_id: card_internal_id, title: title, tags: tags,
|
|
51
|
+
branch: branch, worktree_path: worktree_path, project_config: project_config, project_key: project_key,
|
|
52
|
+
agent_name: assigned_agent, model: detect_model(project_config, tags: tags),
|
|
53
|
+
effort: detect_effort(project_config, tags: tags), cli_provider_override: detect_cli_provider(tags: tags)
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def ignore_assignment(reason, assignee_names, local_names)
|
|
58
|
+
LOG.info "[Fizzy] No local agent matched. Assignees: [#{assignee_names}], Local: [#{local_names.join(", ")}]"
|
|
59
|
+
[200, { status: "ignored", reason: reason }.to_json]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def ignore_unauthorized(payload, eventable)
|
|
63
|
+
creator_name = payload.dig("creator", "name") || "Unknown"
|
|
64
|
+
notify_unauthorized("card_assigned", creator_name, "card ##{eventable["number"]}")
|
|
65
|
+
[200, { status: "ignored", reason: "unauthorized" }.to_json]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def react_to_assignment(card_number, repo_path, agent_name)
|
|
69
|
+
Thread.new do
|
|
70
|
+
run_cmd("fizzy", "reaction", "create", "--card", card_number.to_s,
|
|
71
|
+
"--content", "👍", chdir: repo_path, env: fizzy_env_for(agent_name))
|
|
72
|
+
rescue StandardError => e
|
|
73
|
+
LOG.warn "Could not add reaction to card: #{e.message}"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def setup_assigned_worktree(repo_path, branch, card_internal_id, card_number, project_key, agent_name)
|
|
78
|
+
debounced_repo_fetch(repo_path)
|
|
79
|
+
worktree_path = File.join(File.dirname(repo_path), "#{File.basename(repo_path)}--#{branch}")
|
|
80
|
+
worktree_path = create_or_reuse_worktree(repo_path: repo_path, branch: branch, worktree_path: worktree_path)
|
|
81
|
+
|
|
82
|
+
map = load_work_item_map
|
|
83
|
+
map[card_internal_id] = {
|
|
84
|
+
"number" => card_number, "branch" => branch, "worktree" => worktree_path,
|
|
85
|
+
"project" => project_key, "agent" => agent_name
|
|
86
|
+
}
|
|
87
|
+
save_work_item_map(map)
|
|
88
|
+
worktree_path
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def dispatch_assigned_card(card_number:, card_internal_id:, title:, tags:, branch:, worktree_path:,
|
|
92
|
+
project_config:, project_key:, agent_name:, model:, effort:, cli_provider_override:)
|
|
93
|
+
card_context = prefetch_card_context(card_number, repo_path: project_config["repo_path"], agent_name: agent_name)
|
|
94
|
+
planning_info = detect_planning_mode(text: title, tags: tags, card_internal_id: card_internal_id, card_number: card_number)
|
|
95
|
+
|
|
96
|
+
template_vars = {
|
|
97
|
+
"CARD_NUMBER" => card_number, "CARD_TITLE" => title,
|
|
98
|
+
"BRANCH" => branch, "COMMENT_CREATOR" => agent_name
|
|
99
|
+
}
|
|
100
|
+
brain_ctx = build_brain_context(
|
|
101
|
+
agent_name: agent_name, card_title: title,
|
|
102
|
+
card_number: card_number, project_key: project_key, source: :fizzy
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
prompt = if planning_info
|
|
106
|
+
LOG.info "[Planning] Planning mode active for card ##{card_number}"
|
|
107
|
+
template_vars["CARD_ID"] = planning_info[:card_id]
|
|
108
|
+
render_planning_prompt(PROMPT_CARD_ASSIGNED, template_vars,
|
|
109
|
+
brain_context: brain_ctx, card_context: card_context, agent_name: agent_name)
|
|
110
|
+
else
|
|
111
|
+
template_vars["CARD_ID"] = card_number
|
|
112
|
+
render_prompt(PROMPT_CARD_ASSIGNED, template_vars,
|
|
113
|
+
brain_context: brain_ctx, card_context: card_context, agent_name: agent_name)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
card_key = "card-#{card_number}"
|
|
117
|
+
pid, log_file = run_agent(prompt,
|
|
118
|
+
project_config: project_config, chdir: worktree_path,
|
|
119
|
+
log_name: "assigned-#{card_number}", model: model, effort: effort,
|
|
120
|
+
agent_name: agent_name, card_number: card_number, source: :fizzy,
|
|
121
|
+
source_context: { card_number: card_number },
|
|
122
|
+
cli_provider: cli_provider_override)
|
|
123
|
+
register_session(card_key, pid, log_file: log_file, supersede_key: card_key, agent_name: agent_name)
|
|
124
|
+
|
|
125
|
+
Thread.new { move_card_to_column(card_number, "right_now", project_config: project_config, agent_name: agent_name) }
|
|
126
|
+
|
|
127
|
+
[200, { status: "processed", card: card_number, branch: branch, project: project_key, agent: agent_name }.to_json]
|
|
128
|
+
end
|