brainiac-fizzy 0.0.10 → 0.0.11
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 50d4aa7e239474b5b5046d0d0e98d4b5d1d5aa7faf5c8ff3fe4a69207fa477d2
|
|
4
|
+
data.tar.gz: a622fb92c3fef1d669ea2f910968c2295929a95bb32319541513a47ccdf7addc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4a60a03934e9dd915781e244fb2c8b7d964c4be448d5be686a6f15ada36a6310768f9714c5968cf6dfd27328dc4c409aac59d3b154a32a661f1c40e48aa5373c
|
|
7
|
+
data.tar.gz: 965e79fa73936549609c41db26c35cb8bce914362df9a3a7dd5785f6e2edddce8c185e4435c946157fd8a9ef780291cadf5dee1deb104b5de5f67eea34a3633f
|
|
@@ -134,6 +134,59 @@ PROMPT_FOLLOWUP_WORKTREE = Brainiac::Plugins::Fizzy::Prompts::FOLLOWUP_WORKTREE
|
|
|
134
134
|
PROMPT_FOLLOWUP_NO_WORKTREE = Brainiac::Plugins::Fizzy::Prompts::FOLLOWUP_NO_WORKTREE
|
|
135
135
|
PROMPT_MENTION = Brainiac::Plugins::Fizzy::Prompts::MENTION
|
|
136
136
|
PROMPT_CROSS_AGENT_REVIEW = Brainiac::Plugins::Fizzy::Prompts::CROSS_AGENT_REVIEW
|
|
137
|
+
PROMPT_PLANNING_MODE = Brainiac::Plugins::Fizzy::Prompts::PLANNING_MODE
|
|
138
|
+
|
|
139
|
+
# Render planning mode prompt — identical to render_prompt but inserts the planning
|
|
140
|
+
# instructions between PROMPT_CORE and the channel rules. This was originally defined
|
|
141
|
+
# in brainiac core's planning.rb but belongs here after the fizzy extraction.
|
|
142
|
+
def render_planning_prompt(situation_template, vars = {}, brain_context: "", card_context: "", agent_name: AI_AGENT_NAME,
|
|
143
|
+
channel: :fizzy, board_key: nil)
|
|
144
|
+
plans_dir = Brainiac::Plugins::Fizzy::Planning::PLANS_DIR
|
|
145
|
+
plan_file = File.join(plans_dir, "card-#{vars["CARD_ID"]}-plan.md")
|
|
146
|
+
|
|
147
|
+
result = ""
|
|
148
|
+
result += "#{brain_context}\n" unless brain_context.empty?
|
|
149
|
+
result += card_context unless card_context.empty?
|
|
150
|
+
result += PROMPT_CORE
|
|
151
|
+
result += PROMPT_PLANNING_MODE.gsub("{{PLAN_FILE}}", plan_file)
|
|
152
|
+
result += CHANNEL_PROMPTS.fetch(channel, PROMPT_FIZZY_CHANNEL)
|
|
153
|
+
result += situation_template
|
|
154
|
+
|
|
155
|
+
# Pre-post comment check (same as render_prompt)
|
|
156
|
+
case channel
|
|
157
|
+
when :fizzy then result += PROMPT_PRE_POST_CHECK_FIZZY
|
|
158
|
+
when :github then result += PROMPT_PRE_POST_CHECK_GITHUB
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
result += PROMPT_REFLECTION
|
|
162
|
+
|
|
163
|
+
planning_vars = vars.merge("PLAN_FILE" => plan_file)
|
|
164
|
+
planning_vars["KNOWLEDGE_DIR"] ||= KNOWLEDGE_DIR
|
|
165
|
+
planning_vars["MEMORY_DIR"] ||= memory_dir_for(agent_name)
|
|
166
|
+
planning_vars["PERSONA_DIR"] ||= persona_dir_for(agent_name)
|
|
167
|
+
planning_vars["PERSONA_COLLECTION"] ||= persona_collection_for(agent_name)
|
|
168
|
+
planning_vars["AGENT_NAME"] ||= agent_name
|
|
169
|
+
|
|
170
|
+
# Populate column IDs from board config, falling back to defaults
|
|
171
|
+
DEFAULT_COLUMN_IDS.each do |col_name, default_id|
|
|
172
|
+
var_name = "#{col_name.upcase}_COLUMN_ID"
|
|
173
|
+
planning_vars[var_name] ||= (board_key && board_column_id(board_key, col_name)) || default_id
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Touch memory file if CARD_ID is present — ensures file exists before agent tries to read it
|
|
177
|
+
if vars["CARD_ID"]
|
|
178
|
+
memory_file = File.join(planning_vars["MEMORY_DIR"], "card-#{vars["CARD_ID"]}.md")
|
|
179
|
+
FileUtils.mkdir_p(planning_vars["MEMORY_DIR"])
|
|
180
|
+
FileUtils.touch(memory_file)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
roster = agent_roster
|
|
184
|
+
roster_lines = roster.map { |_key, display| " - @#{display}" }.join("\n")
|
|
185
|
+
planning_vars["AGENT_ROSTER"] ||= roster_lines
|
|
186
|
+
|
|
187
|
+
planning_vars.each { |key, val| result.gsub!("{{#{key}}}", val.to_s) }
|
|
188
|
+
result
|
|
189
|
+
end
|
|
137
190
|
|
|
138
191
|
# Config constants — handler files reference these as top-level constants.
|
|
139
192
|
# These are evaluated after Config.load! has been called (during plugin register).
|
|
@@ -17,7 +17,7 @@ CommentContext = Struct.new(
|
|
|
17
17
|
:mentioned_agent, :agent_name, :is_cross_agent_mention,
|
|
18
18
|
:project_config, :project_key, :card_number, :worktree,
|
|
19
19
|
:model, :effort, :deploy_intent, :cli_provider_override,
|
|
20
|
-
:comment_vars, :card_tags, :worktree_override,
|
|
20
|
+
:comment_vars, :card_tags, :worktree_override, :fresh,
|
|
21
21
|
keyword_init: true
|
|
22
22
|
)
|
|
23
23
|
|
|
@@ -70,6 +70,21 @@ def handle_comment(payload)
|
|
|
70
70
|
project_config: project_config, project_key: project_key
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
+
# --- Intent gate (mirrors brainiac-discord pattern) ---
|
|
74
|
+
# "Directly addressed" for Fizzy: agent is assigned to the card OR explicitly @mentioned.
|
|
75
|
+
# All current dispatch paths satisfy one of these, so intent is always bypassed today.
|
|
76
|
+
# This infrastructure supports future scenarios where agents observe cards they aren't
|
|
77
|
+
# assigned to or mentioned on (ambient watchers).
|
|
78
|
+
directly_addressed = mentioned_agent || (card_info && card_info["agent"]&.downcase == agent_name.downcase)
|
|
79
|
+
unless directly_addressed
|
|
80
|
+
card_number_for_intent = card_info&.dig("number") || eventable.dig("card", "number")
|
|
81
|
+
intent_ctx = card_number_for_intent ? fetch_intent_context(card_number_for_intent, repo_path: project_config["repo_path"], agent_name: agent_name) : nil
|
|
82
|
+
if intent_skip?(ctx.plain_text, agent_name: agent_name, source: :fizzy,
|
|
83
|
+
channel: "Fizzy card ##{card_number_for_intent || card_internal_id}", context: intent_ctx)
|
|
84
|
+
return [200, { status: "intent_skip", agent: agent_name }.to_json]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
73
88
|
# --- Route to appropriate sub-handler ---
|
|
74
89
|
if is_cross_agent_mention
|
|
75
90
|
handle_cross_agent_mention(ctx)
|
|
@@ -126,6 +141,7 @@ def build_comment_context(eventable:, plain_text:, tags:, card_internal_id:, car
|
|
|
126
141
|
cli_provider_override: effective_cli,
|
|
127
142
|
card_tags: card_tags,
|
|
128
143
|
worktree_override: resolve_worktree_override(tags, project_config),
|
|
144
|
+
fresh: tags[:fresh],
|
|
129
145
|
comment_vars: {
|
|
130
146
|
"COMMENT_CREATOR" => creator_name || "Unknown",
|
|
131
147
|
"COMMENT_ID" => comment_id.to_s,
|
|
@@ -385,17 +401,14 @@ def dispatch_cross_agent_review(ctx, card_key:, card_number:, card_assigned_agen
|
|
|
385
401
|
review_worktree_path, review_branch = setup_cross_agent_worktree(ctx, card_number)
|
|
386
402
|
prompt = build_cross_agent_prompt(ctx, card_number, card_assigned_agent, review_worktree_path, review_branch)
|
|
387
403
|
|
|
388
|
-
intent_ctx = fetch_intent_context(card_number, repo_path: ctx.project_config["repo_path"], agent_name: ctx.agent_name)
|
|
389
404
|
pid, log_file = run_agent(prompt,
|
|
390
405
|
project_config: ctx.project_config, chdir: review_worktree_path,
|
|
391
406
|
log_name: "review-#{ctx.agent_name.downcase}-#{card_number || ctx.card_internal_id}",
|
|
392
407
|
model: ctx.model, effort: ctx.effort, agent_name: ctx.agent_name,
|
|
393
408
|
card_number: card_number, comment_id: ctx.comment_id,
|
|
394
409
|
source: :fizzy, source_context: { card_number: card_number },
|
|
395
|
-
cli_provider: ctx.cli_provider_override
|
|
396
|
-
|
|
397
|
-
context: intent_ctx)
|
|
398
|
-
return [200, { status: "intent_skip", agent: ctx.agent_name, card: card_number }.to_json] unless pid
|
|
410
|
+
cli_provider: ctx.cli_provider_override)
|
|
411
|
+
return [200, { status: "dispatch_failed", agent: ctx.agent_name, card: card_number }.to_json] unless pid
|
|
399
412
|
|
|
400
413
|
register_session(card_key, pid, log_file: log_file, supersede_key: card_key, agent_name: ctx.agent_name)
|
|
401
414
|
|
|
@@ -493,17 +506,14 @@ end
|
|
|
493
506
|
def dispatch_new_mention(ctx, card_key:, card_number:, card_title:, branch:, worktree_path:)
|
|
494
507
|
prompt = build_mention_prompt(ctx, card_number, card_title, branch, worktree_path)
|
|
495
508
|
|
|
496
|
-
intent_ctx = fetch_intent_context(card_number, repo_path: ctx.project_config["repo_path"], agent_name: ctx.agent_name)
|
|
497
509
|
pid, log_file = run_agent(prompt,
|
|
498
510
|
project_config: ctx.project_config, chdir: worktree_path,
|
|
499
511
|
log_name: "mention-#{card_number || ctx.card_internal_id}",
|
|
500
512
|
model: ctx.model, effort: ctx.effort, agent_name: ctx.agent_name,
|
|
501
513
|
card_number: card_number, comment_id: ctx.comment_id,
|
|
502
514
|
source: :fizzy, cli_provider: ctx.cli_provider_override,
|
|
503
|
-
source_context: { card_number: card_number }
|
|
504
|
-
|
|
505
|
-
context: intent_ctx)
|
|
506
|
-
return [200, { status: "intent_skip", agent: ctx.agent_name, card: card_number }.to_json] unless pid
|
|
515
|
+
source_context: { card_number: card_number })
|
|
516
|
+
return [200, { status: "dispatch_failed", agent: ctx.agent_name, card: card_number }.to_json] unless pid
|
|
507
517
|
|
|
508
518
|
register_session(card_key, pid, log_file: log_file, supersede_key: card_key, agent_name: ctx.agent_name)
|
|
509
519
|
|
|
@@ -517,11 +527,15 @@ def dispatch_followup_comment(ctx, card_key:, card_number:, work_dir:)
|
|
|
517
527
|
effort = detect_effort(ctx.project_config, tags: card_tags, text: ctx.plain_text)
|
|
518
528
|
|
|
519
529
|
is_worktree = work_dir != ctx.project_config["repo_path"]
|
|
520
|
-
should_resume = is_worktree && resume_viable?(
|
|
530
|
+
should_resume = is_worktree && !ctx.fresh && resume_viable?(
|
|
521
531
|
project_config: ctx.project_config, cli_provider: ctx.cli_provider_override,
|
|
522
532
|
agent_name: ctx.agent_name, chdir: work_dir
|
|
523
533
|
)
|
|
524
534
|
|
|
535
|
+
if ctx.fresh && is_worktree
|
|
536
|
+
LOG.info "[Fizzy] [fresh] tag — forcing new session instead of resuming on card #{card_number || ctx.card_internal_id}"
|
|
537
|
+
end
|
|
538
|
+
|
|
525
539
|
prompt = if should_resume
|
|
526
540
|
LOG.info "[Resume] Using lean prompt for follow-up on card #{card_number || ctx.card_internal_id}"
|
|
527
541
|
render_resume_prompt(
|
|
@@ -532,7 +546,6 @@ def dispatch_followup_comment(ctx, card_key:, card_number:, work_dir:)
|
|
|
532
546
|
build_followup_prompt(ctx, card_number, card_tags, work_dir)
|
|
533
547
|
end
|
|
534
548
|
|
|
535
|
-
intent_ctx = fetch_intent_context(card_number, repo_path: ctx.project_config["repo_path"], agent_name: ctx.agent_name)
|
|
536
549
|
pid, log_file = run_agent(prompt,
|
|
537
550
|
project_config: ctx.project_config, chdir: work_dir,
|
|
538
551
|
log_name: "followup-#{card_number || ctx.card_internal_id}",
|
|
@@ -542,10 +555,8 @@ def dispatch_followup_comment(ctx, card_key:, card_number:, work_dir:)
|
|
|
542
555
|
source_context: {
|
|
543
556
|
card_number: card_number, card_internal_id: ctx.card_internal_id,
|
|
544
557
|
deploy_intent: ctx.deploy_intent
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
context: intent_ctx)
|
|
548
|
-
return [200, { status: "intent_skip", agent: ctx.agent_name, card: card_number }.to_json] unless pid
|
|
558
|
+
})
|
|
559
|
+
return [200, { status: "dispatch_failed", agent: ctx.agent_name, card: card_number }.to_json] unless pid
|
|
549
560
|
|
|
550
561
|
register_session(card_key, pid, log_file: log_file, supersede_key: card_key, agent_name: ctx.agent_name)
|
|
551
562
|
|
|
@@ -116,6 +116,95 @@ module Brainiac
|
|
|
116
116
|
Run any UAT testing steps defined in the card or acceptance criteria.
|
|
117
117
|
Post results as a comment on the card.
|
|
118
118
|
PROMPT
|
|
119
|
+
|
|
120
|
+
PLANNING_MODE = <<~PROMPT
|
|
121
|
+
## Planning Mode (ACTIVE)
|
|
122
|
+
|
|
123
|
+
You are in **planning mode**. Your job is to gather requirements and break down the work into actionable tasks.
|
|
124
|
+
|
|
125
|
+
### Your Role
|
|
126
|
+
- Ask clarifying questions to understand the problem, constraints, and desired outcome
|
|
127
|
+
- Continue asking until you have a clear picture (don't rush to a plan)
|
|
128
|
+
- Understand user intent naturally — "go ahead", "that's enough", "proceed" all mean the same thing
|
|
129
|
+
- When you have enough information OR the user signals they're ready, generate the plan
|
|
130
|
+
|
|
131
|
+
### Question Guidelines
|
|
132
|
+
- Ask specific, focused questions (not generic "anything else?")
|
|
133
|
+
- Build on previous answers — reference what you've learned
|
|
134
|
+
- Prioritize questions that would significantly change the approach
|
|
135
|
+
- If you're 90% confident, proceed. If you're 60% confident, ask.
|
|
136
|
+
|
|
137
|
+
### When to Stop Asking
|
|
138
|
+
The user will signal they're ready in natural language:
|
|
139
|
+
- "go ahead", "proceed", "that's enough", "looks good", "yeah do it"
|
|
140
|
+
- "I think you have enough", "start working", "make the plan"
|
|
141
|
+
|
|
142
|
+
You should also stop if:
|
|
143
|
+
- You've asked 5+ questions and have a clear understanding
|
|
144
|
+
- The user is getting impatient or frustrated
|
|
145
|
+
- The remaining unknowns are minor details you can decide yourself
|
|
146
|
+
|
|
147
|
+
### Generating the Plan
|
|
148
|
+
When ready, create a plan file at `{{PLAN_FILE}}` with this structure:
|
|
149
|
+
|
|
150
|
+
```markdown
|
|
151
|
+
# Feature: [Title]
|
|
152
|
+
|
|
153
|
+
## Problem Statement
|
|
154
|
+
[What we're solving and why]
|
|
155
|
+
|
|
156
|
+
## Requirements
|
|
157
|
+
- Requirement 1
|
|
158
|
+
- Requirement 2
|
|
159
|
+
- Requirement 3
|
|
160
|
+
|
|
161
|
+
## Approach
|
|
162
|
+
[High-level strategy and key decisions]
|
|
163
|
+
|
|
164
|
+
## Task Breakdown
|
|
165
|
+
### Task 1: [Clear, actionable title]
|
|
166
|
+
- **Objective**: [What this task accomplishes]
|
|
167
|
+
- **Approach**: [How to implement it]
|
|
168
|
+
- **Demo**: [What "done" looks like]
|
|
169
|
+
|
|
170
|
+
### Task 2: [Clear, actionable title]
|
|
171
|
+
- **Objective**: [What this task accomplishes]
|
|
172
|
+
- **Approach**: [How to implement it]
|
|
173
|
+
- **Demo**: [What "done" looks like]
|
|
174
|
+
|
|
175
|
+
[Continue for all tasks...]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Memory Management
|
|
179
|
+
Log every question and answer to your memory file in this format:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
## Planning Q&A
|
|
183
|
+
Q: [Your question]
|
|
184
|
+
A: [User's answer]
|
|
185
|
+
|
|
186
|
+
Q: [Next question]
|
|
187
|
+
A: [User's answer]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Also track:
|
|
191
|
+
- `planning_complete: false` (update to `true` when plan is generated)
|
|
192
|
+
- Key decisions and constraints discovered
|
|
193
|
+
- Any blockers or unknowns that remain
|
|
194
|
+
|
|
195
|
+
### After Planning
|
|
196
|
+
Once you've written the plan file:
|
|
197
|
+
1. Update memory with `planning_complete: true`
|
|
198
|
+
2. Post a comment summarizing the plan and linking to the file
|
|
199
|
+
3. The system will automatically create Fizzy steps from your task breakdown
|
|
200
|
+
|
|
201
|
+
### Important
|
|
202
|
+
- You are READ-ONLY during planning — no code changes, no commits
|
|
203
|
+
- Focus on understanding the problem, not solving it yet
|
|
204
|
+
- The plan should be detailed enough that any agent could execute it
|
|
205
|
+
- Task titles should be clear and actionable (they become Fizzy step names)
|
|
206
|
+
|
|
207
|
+
PROMPT
|
|
119
208
|
end
|
|
120
209
|
end
|
|
121
210
|
end
|