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: 58da7bc4b4b3365df978936518462a054cd9aa1f4b1715cfdb45c41820c10cac
4
- data.tar.gz: e3b3c47d4bbc5f03e58d99fb5281fc67ababeebf299ed78d505cd831556e36f1
3
+ metadata.gz: 50d4aa7e239474b5b5046d0d0e98d4b5d1d5aa7faf5c8ff3fe4a69207fa477d2
4
+ data.tar.gz: a622fb92c3fef1d669ea2f910968c2295929a95bb32319541513a47ccdf7addc
5
5
  SHA512:
6
- metadata.gz: 0cd98dc6c45a30b806cb8da059fee1e8d49d36032fba262e0cbc9d5e82d563daa6f32bbd6b5419a3f7ae9b139ecc9613f07b93202edcb8dfb44f823e2b8867af
7
- data.tar.gz: 7cd864aa4ab8b418052ce1c251b17f89bab7aa1fe2163efb6df3bab7df5629d57386180fe52f5d6d6983d2c9910fdd6a27fcc0bdf650e0e1186798b388cdec7f
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
- message: ctx.plain_text, channel: "Fizzy comment",
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
- message: ctx.plain_text, channel: "Fizzy comment",
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
- message: ctx.plain_text, channel: "Fizzy comment",
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
@@ -3,7 +3,7 @@
3
3
  module Brainiac
4
4
  module Plugins
5
5
  module Fizzy
6
- VERSION = "0.0.10"
6
+ VERSION = "0.0.11"
7
7
  end
8
8
  end
9
9
  end
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.10
4
+ version: 0.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis