stravinsky 0.2.40__py3-none-any.whl → 0.3.4__py3-none-any.whl
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.
- mcp_bridge/__init__.py +1 -1
- mcp_bridge/auth/token_refresh.py +130 -0
- mcp_bridge/cli/__init__.py +6 -0
- mcp_bridge/cli/install_hooks.py +1265 -0
- mcp_bridge/cli/session_report.py +585 -0
- mcp_bridge/hooks/HOOKS_SETTINGS.json +175 -0
- mcp_bridge/hooks/README.md +215 -0
- mcp_bridge/hooks/__init__.py +119 -43
- mcp_bridge/hooks/edit_recovery.py +42 -37
- mcp_bridge/hooks/git_noninteractive.py +89 -0
- mcp_bridge/hooks/keyword_detector.py +30 -0
- mcp_bridge/hooks/manager.py +50 -0
- mcp_bridge/hooks/notification_hook.py +103 -0
- mcp_bridge/hooks/parallel_enforcer.py +127 -0
- mcp_bridge/hooks/parallel_execution.py +111 -0
- mcp_bridge/hooks/pre_compact.py +123 -0
- mcp_bridge/hooks/preemptive_compaction.py +81 -7
- mcp_bridge/hooks/rules_injector.py +507 -0
- mcp_bridge/hooks/session_idle.py +116 -0
- mcp_bridge/hooks/session_notifier.py +125 -0
- mcp_bridge/{native_hooks → hooks}/stravinsky_mode.py +51 -16
- mcp_bridge/hooks/subagent_stop.py +98 -0
- mcp_bridge/hooks/task_validator.py +73 -0
- mcp_bridge/hooks/tmux_manager.py +141 -0
- mcp_bridge/hooks/todo_continuation.py +90 -0
- mcp_bridge/hooks/todo_delegation.py +88 -0
- mcp_bridge/hooks/tool_messaging.py +164 -0
- mcp_bridge/hooks/truncator.py +21 -17
- mcp_bridge/notifications.py +151 -0
- mcp_bridge/prompts/__init__.py +3 -1
- mcp_bridge/prompts/dewey.py +30 -20
- mcp_bridge/prompts/explore.py +46 -8
- mcp_bridge/prompts/multimodal.py +24 -3
- mcp_bridge/prompts/planner.py +222 -0
- mcp_bridge/prompts/stravinsky.py +107 -28
- mcp_bridge/server.py +170 -10
- mcp_bridge/server_tools.py +554 -32
- mcp_bridge/tools/agent_manager.py +316 -106
- mcp_bridge/tools/background_tasks.py +2 -1
- mcp_bridge/tools/code_search.py +97 -11
- mcp_bridge/tools/lsp/__init__.py +7 -0
- mcp_bridge/tools/lsp/manager.py +448 -0
- mcp_bridge/tools/lsp/tools.py +637 -150
- mcp_bridge/tools/model_invoke.py +270 -47
- mcp_bridge/tools/semantic_search.py +2492 -0
- mcp_bridge/tools/templates.py +32 -18
- stravinsky-0.3.4.dist-info/METADATA +420 -0
- stravinsky-0.3.4.dist-info/RECORD +79 -0
- stravinsky-0.3.4.dist-info/entry_points.txt +5 -0
- mcp_bridge/native_hooks/edit_recovery.py +0 -46
- mcp_bridge/native_hooks/truncator.py +0 -23
- stravinsky-0.2.40.dist-info/METADATA +0 -204
- stravinsky-0.2.40.dist-info/RECORD +0 -57
- stravinsky-0.2.40.dist-info/entry_points.txt +0 -3
- /mcp_bridge/{native_hooks → hooks}/context.py +0 -0
- {stravinsky-0.2.40.dist-info → stravinsky-0.3.4.dist-info}/WHEEL +0 -0
|
@@ -11,6 +11,7 @@ import os
|
|
|
11
11
|
import shutil
|
|
12
12
|
import subprocess
|
|
13
13
|
import signal
|
|
14
|
+
import time
|
|
14
15
|
import uuid
|
|
15
16
|
from dataclasses import asdict, dataclass, field
|
|
16
17
|
from datetime import datetime
|
|
@@ -21,6 +22,85 @@ import logging
|
|
|
21
22
|
|
|
22
23
|
logger = logging.getLogger(__name__)
|
|
23
24
|
|
|
25
|
+
# Model routing configuration
|
|
26
|
+
# Specialized agents call external models via MCP tools:
|
|
27
|
+
# explore/dewey/document_writer/multimodal → invoke_gemini(gemini-3-flash)
|
|
28
|
+
# frontend → invoke_gemini(gemini-3-pro-high)
|
|
29
|
+
# delphi → invoke_openai(gpt-5.2)
|
|
30
|
+
# Non-specialized coding tasks use Claude CLI with --model sonnet
|
|
31
|
+
AGENT_MODEL_ROUTING = {
|
|
32
|
+
# Specialized agents - no CLI model flag, they call invoke_* tools
|
|
33
|
+
"explore": None,
|
|
34
|
+
"dewey": None,
|
|
35
|
+
"document_writer": None,
|
|
36
|
+
"multimodal": None,
|
|
37
|
+
"frontend": None,
|
|
38
|
+
"delphi": None,
|
|
39
|
+
"research-lead": None, # Hierarchical orchestrator using gemini-3-flash
|
|
40
|
+
"implementation-lead": None, # Hierarchical orchestrator using haiku
|
|
41
|
+
# Planner uses Opus for superior reasoning about dependencies and parallelization
|
|
42
|
+
"planner": "opus",
|
|
43
|
+
# Default for unknown agent types (coding tasks) - use Sonnet 4.5
|
|
44
|
+
"_default": "sonnet",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Cost tier classification (from oh-my-opencode pattern)
|
|
48
|
+
AGENT_COST_TIERS = {
|
|
49
|
+
"explore": "CHEAP", # Uses gemini-3-flash
|
|
50
|
+
"dewey": "CHEAP", # Uses gemini-3-flash
|
|
51
|
+
"document_writer": "CHEAP", # Uses gemini-3-flash
|
|
52
|
+
"multimodal": "CHEAP", # Uses gemini-3-flash
|
|
53
|
+
"research-lead": "CHEAP", # Uses gemini-3-flash
|
|
54
|
+
"implementation-lead": "CHEAP", # Uses haiku
|
|
55
|
+
"frontend": "MEDIUM", # Uses gemini-3-pro-high
|
|
56
|
+
"delphi": "EXPENSIVE", # Uses gpt-5.2 (OpenAI GPT)
|
|
57
|
+
"planner": "EXPENSIVE", # Uses Claude Opus 4.5
|
|
58
|
+
"_default": "EXPENSIVE", # Claude Sonnet 4.5 via CLI
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Display model names for output formatting (user-visible)
|
|
62
|
+
AGENT_DISPLAY_MODELS = {
|
|
63
|
+
"explore": "gemini-3-flash",
|
|
64
|
+
"dewey": "gemini-3-flash",
|
|
65
|
+
"document_writer": "gemini-3-flash",
|
|
66
|
+
"multimodal": "gemini-3-flash",
|
|
67
|
+
"research-lead": "gemini-3-flash",
|
|
68
|
+
"implementation-lead": "haiku",
|
|
69
|
+
"frontend": "gemini-3-pro-high",
|
|
70
|
+
"delphi": "gpt-5.2",
|
|
71
|
+
"planner": "opus-4.5",
|
|
72
|
+
"_default": "sonnet-4.5",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Cost tier emoji indicators for visual differentiation
|
|
76
|
+
# Colors indicate cost: 🟢 cheap/free, 🔵 medium, 🟣 expensive (GPT), 🟠 Claude
|
|
77
|
+
COST_TIER_EMOJI = {
|
|
78
|
+
"CHEAP": "🟢", # Free/cheap models (gemini-3-flash, haiku)
|
|
79
|
+
"MEDIUM": "🔵", # Medium cost (gemini-3-pro-high)
|
|
80
|
+
"EXPENSIVE": "🟣", # Expensive models (gpt-5.2, opus)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Model family indicators
|
|
84
|
+
MODEL_FAMILY_EMOJI = {
|
|
85
|
+
"gemini-3-flash": "🟢",
|
|
86
|
+
"gemini-3-pro-high": "🔵",
|
|
87
|
+
"haiku": "🟢",
|
|
88
|
+
"sonnet-4.5": "🟠",
|
|
89
|
+
"opus-4.5": "🟣",
|
|
90
|
+
"gpt-5.2": "🟣",
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_agent_emoji(agent_type: str) -> str:
|
|
95
|
+
"""Get the colored emoji indicator for an agent based on its cost tier."""
|
|
96
|
+
cost_tier = AGENT_COST_TIERS.get(agent_type, AGENT_COST_TIERS["_default"])
|
|
97
|
+
return COST_TIER_EMOJI.get(cost_tier, "⚪")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_model_emoji(model_name: str) -> str:
|
|
101
|
+
"""Get the colored emoji indicator for a model."""
|
|
102
|
+
return MODEL_FAMILY_EMOJI.get(model_name, "⚪")
|
|
103
|
+
|
|
24
104
|
|
|
25
105
|
@dataclass
|
|
26
106
|
class AgentTask:
|
|
@@ -86,7 +166,7 @@ class AgentManager:
|
|
|
86
166
|
|
|
87
167
|
# In-memory tracking for running processes
|
|
88
168
|
self._processes: Dict[str, subprocess.Popen] = {}
|
|
89
|
-
self._notification_queue: Dict[str, List[
|
|
169
|
+
self._notification_queue: Dict[str, List[Dict[str, Any]]] = {}
|
|
90
170
|
|
|
91
171
|
def _load_tasks(self) -> Dict[str, Any]:
|
|
92
172
|
"""Load tasks from persistent storage."""
|
|
@@ -155,7 +235,9 @@ class AgentManager:
|
|
|
155
235
|
Returns:
|
|
156
236
|
Task ID for tracking
|
|
157
237
|
"""
|
|
158
|
-
|
|
238
|
+
import uuid as uuid_module # Local import for MCP context
|
|
239
|
+
|
|
240
|
+
task_id = f"agent_{uuid_module.uuid4().hex[:8]}"
|
|
159
241
|
|
|
160
242
|
task = AgentTask(
|
|
161
243
|
id=task_id,
|
|
@@ -220,12 +302,20 @@ class AgentManager:
|
|
|
220
302
|
full_prompt,
|
|
221
303
|
"--output-format",
|
|
222
304
|
"text",
|
|
305
|
+
"--dangerously-skip-permissions", # Critical: bypass permission prompts
|
|
223
306
|
]
|
|
224
307
|
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
|
|
308
|
+
# Model routing:
|
|
309
|
+
# - Specialized agents (explore/dewey/etc): None = use CLI default, they call invoke_*
|
|
310
|
+
# - Unknown agent types (coding tasks): Use Sonnet 4.5
|
|
311
|
+
if agent_type in AGENT_MODEL_ROUTING:
|
|
312
|
+
cli_model = AGENT_MODEL_ROUTING[agent_type] # None for specialized
|
|
313
|
+
else:
|
|
314
|
+
cli_model = AGENT_MODEL_ROUTING.get("_default", "sonnet")
|
|
315
|
+
|
|
316
|
+
if cli_model:
|
|
317
|
+
cmd.extend(["--model", cli_model])
|
|
318
|
+
logger.info(f"[AgentManager] Using --model {cli_model} for {agent_type} agent")
|
|
229
319
|
|
|
230
320
|
# Add system prompt file if we have one
|
|
231
321
|
if system_prompt:
|
|
@@ -240,6 +330,7 @@ class AgentManager:
|
|
|
240
330
|
# (Previously used file handle which was closed before process finished)
|
|
241
331
|
process = subprocess.Popen(
|
|
242
332
|
cmd,
|
|
333
|
+
stdin=subprocess.DEVNULL, # Critical: prevent stdin blocking
|
|
243
334
|
stdout=subprocess.PIPE,
|
|
244
335
|
stderr=subprocess.PIPE,
|
|
245
336
|
text=True,
|
|
@@ -416,20 +507,28 @@ class AgentManager:
|
|
|
416
507
|
start = datetime.now()
|
|
417
508
|
while (datetime.now() - start).total_seconds() < timeout:
|
|
418
509
|
task = self.get_task(task_id)
|
|
419
|
-
if task["status"] != "running":
|
|
510
|
+
if not task or task["status"] != "running":
|
|
420
511
|
break
|
|
421
|
-
|
|
512
|
+
time.sleep(0.5)
|
|
513
|
+
|
|
514
|
+
# Refresh task state after potential blocking wait
|
|
515
|
+
if not task:
|
|
516
|
+
return f"Task {task_id} not found."
|
|
422
517
|
|
|
423
518
|
status = task["status"]
|
|
424
519
|
description = task.get("description", "")
|
|
425
520
|
agent_type = task.get("agent_type", "unknown")
|
|
426
521
|
|
|
522
|
+
# Get cost-tier emoji for visual differentiation
|
|
523
|
+
cost_emoji = get_agent_emoji(agent_type)
|
|
524
|
+
display_model = AGENT_DISPLAY_MODELS.get(agent_type, AGENT_DISPLAY_MODELS["_default"])
|
|
525
|
+
|
|
427
526
|
if status == "completed":
|
|
428
527
|
result = task.get("result", "(no output)")
|
|
429
|
-
return f"""✅ Agent Task Completed
|
|
528
|
+
return f"""{cost_emoji} ✅ Agent Task Completed
|
|
430
529
|
|
|
431
530
|
**Task ID**: {task_id}
|
|
432
|
-
**Agent**: {agent_type}
|
|
531
|
+
**Agent**: {agent_type}:{display_model}
|
|
433
532
|
**Description**: {description}
|
|
434
533
|
|
|
435
534
|
**Result**:
|
|
@@ -437,29 +536,29 @@ class AgentManager:
|
|
|
437
536
|
|
|
438
537
|
elif status == "failed":
|
|
439
538
|
error = task.get("error", "(no error details)")
|
|
440
|
-
return f"""❌ Agent Task Failed
|
|
539
|
+
return f"""{cost_emoji} ❌ Agent Task Failed
|
|
441
540
|
|
|
442
541
|
**Task ID**: {task_id}
|
|
443
|
-
**Agent**: {agent_type}
|
|
542
|
+
**Agent**: {agent_type}:{display_model}
|
|
444
543
|
**Description**: {description}
|
|
445
544
|
|
|
446
545
|
**Error**:
|
|
447
546
|
{error}"""
|
|
448
547
|
|
|
449
548
|
elif status == "cancelled":
|
|
450
|
-
return f"""⚠️ Agent Task Cancelled
|
|
549
|
+
return f"""{cost_emoji} ⚠️ Agent Task Cancelled
|
|
451
550
|
|
|
452
551
|
**Task ID**: {task_id}
|
|
453
|
-
**Agent**: {agent_type}
|
|
552
|
+
**Agent**: {agent_type}:{display_model}
|
|
454
553
|
**Description**: {description}"""
|
|
455
554
|
|
|
456
555
|
else: # pending or running
|
|
457
556
|
pid = task.get("pid", "N/A")
|
|
458
557
|
started = task.get("started_at", "N/A")
|
|
459
|
-
return f"""⏳ Agent Task Running
|
|
558
|
+
return f"""{cost_emoji} ⏳ Agent Task Running
|
|
460
559
|
|
|
461
560
|
**Task ID**: {task_id}
|
|
462
|
-
**Agent**: {agent_type}
|
|
561
|
+
**Agent**: {agent_type}:{display_model}
|
|
463
562
|
**Description**: {description}
|
|
464
563
|
**PID**: {pid}
|
|
465
564
|
**Started**: {started}
|
|
@@ -535,10 +634,14 @@ Use `agent_output` with block=true to wait for completion."""
|
|
|
535
634
|
"cancelled": "⚠️",
|
|
536
635
|
}.get(status, "❓")
|
|
537
636
|
|
|
538
|
-
|
|
637
|
+
# Get cost-tier emoji for visual differentiation
|
|
638
|
+
cost_emoji = get_agent_emoji(agent_type)
|
|
639
|
+
display_model = AGENT_DISPLAY_MODELS.get(agent_type, AGENT_DISPLAY_MODELS["_default"])
|
|
640
|
+
|
|
641
|
+
result = f"""{cost_emoji} {status_emoji} **Agent Progress**
|
|
539
642
|
|
|
540
643
|
**Task ID**: {task_id}
|
|
541
|
-
**Agent**: {agent_type}
|
|
644
|
+
**Agent**: {agent_type}:{display_model}
|
|
542
645
|
**Description**: {description}
|
|
543
646
|
**Status**: {status}
|
|
544
647
|
"""
|
|
@@ -583,6 +686,7 @@ async def agent_spawn(
|
|
|
583
686
|
model: str = "gemini-3-flash",
|
|
584
687
|
thinking_budget: int = 0,
|
|
585
688
|
timeout: int = 300,
|
|
689
|
+
blocking: bool = False,
|
|
586
690
|
) -> str:
|
|
587
691
|
"""
|
|
588
692
|
Spawn a background agent.
|
|
@@ -594,95 +698,191 @@ async def agent_spawn(
|
|
|
594
698
|
model: Model to use (gemini-3-flash, gemini-2.0-flash, claude)
|
|
595
699
|
thinking_budget: Reserved reasoning tokens
|
|
596
700
|
timeout: Execution timeout in seconds
|
|
701
|
+
blocking: If True, wait for completion and return result directly (use for delphi)
|
|
597
702
|
|
|
598
703
|
Returns:
|
|
599
|
-
Task ID and instructions
|
|
704
|
+
Task ID and instructions, or full result if blocking=True
|
|
600
705
|
"""
|
|
601
706
|
manager = get_manager()
|
|
602
707
|
|
|
603
708
|
# Map agent types to system prompts
|
|
709
|
+
# ALL agents use invoke_gemini or invoke_openai - NOT Claude directly
|
|
710
|
+
# explore/dewey/document_writer/multimodal/frontend → gemini-3-flash
|
|
711
|
+
# delphi → openai gpt-5.2
|
|
604
712
|
system_prompts = {
|
|
605
|
-
"explore": "You are a codebase exploration specialist. Find files, patterns, and answer 'where is X?' questions
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
-
|
|
671
|
-
-
|
|
713
|
+
"explore": """You are a codebase exploration specialist. Find files, patterns, and answer 'where is X?' questions.
|
|
714
|
+
|
|
715
|
+
MODEL ROUTING (MANDATORY):
|
|
716
|
+
You MUST use invoke_gemini with model="gemini-3-flash" for ALL analysis and reasoning.
|
|
717
|
+
Use Claude's native tools (Read, Grep, Glob) ONLY for file access, then pass content to invoke_gemini.
|
|
718
|
+
|
|
719
|
+
WORKFLOW:
|
|
720
|
+
1. Use Read/Grep/Glob to get file contents
|
|
721
|
+
2. Call invoke_gemini(prompt="Analyze this: <content>", model="gemini-3-flash", agent_context={"agent_type": "explore"}) for analysis
|
|
722
|
+
3. Return the Gemini response""",
|
|
723
|
+
"dewey": """You are a documentation and research specialist. Find implementation examples and official docs.
|
|
724
|
+
|
|
725
|
+
MODEL ROUTING (MANDATORY):
|
|
726
|
+
You MUST use invoke_gemini with model="gemini-3-flash" for ALL analysis, summarization, and reasoning.
|
|
727
|
+
|
|
728
|
+
WORKFLOW:
|
|
729
|
+
1. Gather information using available tools
|
|
730
|
+
2. Call invoke_gemini(prompt="<task>", model="gemini-3-flash", agent_context={"agent_type": "dewey"}) for processing
|
|
731
|
+
3. Return the Gemini response""",
|
|
732
|
+
"frontend": """You are a Senior Frontend Architect & UI Designer.
|
|
733
|
+
|
|
734
|
+
MODEL ROUTING (MANDATORY):
|
|
735
|
+
You MUST use invoke_gemini with model="gemini-3-pro-high" for ALL code generation and design work.
|
|
736
|
+
|
|
737
|
+
DESIGN PHILOSOPHY:
|
|
738
|
+
- Anti-Generic: Reject standard layouts. Bespoke, asymmetric, distinctive.
|
|
739
|
+
- Library Discipline: Use existing UI libraries (Shadcn, Radix, MUI) if detected.
|
|
740
|
+
- Stack: React/Vue/Svelte, Tailwind/Custom CSS, semantic HTML5.
|
|
741
|
+
|
|
742
|
+
WORKFLOW:
|
|
743
|
+
1. Analyze requirements
|
|
744
|
+
2. Call invoke_gemini(prompt="Generate frontend code for: <task>", model="gemini-3-pro-high", agent_context={"agent_type": "frontend"})
|
|
745
|
+
3. Return the code""",
|
|
746
|
+
"delphi": """You are a strategic technical advisor for architecture and hard debugging.
|
|
747
|
+
|
|
748
|
+
MODEL ROUTING (MANDATORY):
|
|
749
|
+
You MUST use invoke_openai with model="gpt-5.2" for ALL strategic advice and analysis.
|
|
750
|
+
|
|
751
|
+
WORKFLOW:
|
|
752
|
+
1. Gather context about the problem
|
|
753
|
+
2. Call invoke_openai(prompt="<problem description>", model="gpt-5.2", agent_context={"agent_type": "delphi"})
|
|
754
|
+
3. Return the GPT response""",
|
|
755
|
+
"document_writer": """You are a Technical Documentation Specialist.
|
|
756
|
+
|
|
757
|
+
MODEL ROUTING (MANDATORY):
|
|
758
|
+
You MUST use invoke_gemini with model="gemini-3-flash" for ALL documentation generation.
|
|
759
|
+
|
|
760
|
+
DOCUMENT TYPES: README, API docs, ADRs, user guides, inline docs.
|
|
761
|
+
|
|
762
|
+
WORKFLOW:
|
|
763
|
+
1. Gather context about what to document
|
|
764
|
+
2. Call invoke_gemini(prompt="Write documentation for: <topic>", model="gemini-3-flash", agent_context={"agent_type": "document_writer"})
|
|
765
|
+
3. Return the documentation""",
|
|
766
|
+
"multimodal": """You interpret media files (PDFs, images, diagrams, screenshots).
|
|
767
|
+
|
|
768
|
+
MODEL ROUTING (MANDATORY):
|
|
769
|
+
You MUST use invoke_gemini with model="gemini-3-flash" for ALL visual analysis.
|
|
770
|
+
|
|
771
|
+
WORKFLOW:
|
|
772
|
+
1. Receive file path and extraction goal
|
|
773
|
+
2. Call invoke_gemini(prompt="Analyze this file: <path>. Extract: <goal>", model="gemini-3-flash", agent_context={"agent_type": "multimodal"})
|
|
774
|
+
3. Return extracted information only""",
|
|
775
|
+
"planner": """You are a pre-implementation planning specialist. You analyze requests and produce structured implementation plans BEFORE any code changes begin.
|
|
776
|
+
|
|
777
|
+
PURPOSE:
|
|
778
|
+
- Analyze requests and produce actionable implementation plans
|
|
779
|
+
- Identify dependencies and parallelization opportunities
|
|
780
|
+
- Enable efficient parallel execution by the orchestrator
|
|
781
|
+
- Prevent wasted effort through upfront planning
|
|
782
|
+
|
|
783
|
+
METHODOLOGY:
|
|
784
|
+
1. EXPLORE FIRST: Spawn explore agents IN PARALLEL to understand the codebase
|
|
785
|
+
2. DECOMPOSE: Break request into atomic, single-purpose tasks
|
|
786
|
+
3. ANALYZE DEPENDENCIES: What blocks what? What can run in parallel?
|
|
787
|
+
4. ASSIGN AGENTS: Map each task to the right specialist (explore/dewey/frontend/delphi)
|
|
788
|
+
5. OUTPUT STRUCTURED PLAN: Use the required format below
|
|
789
|
+
|
|
790
|
+
REQUIRED OUTPUT FORMAT:
|
|
791
|
+
```
|
|
792
|
+
## PLAN: [Brief title]
|
|
793
|
+
|
|
794
|
+
### ANALYSIS
|
|
795
|
+
- **Request**: [One sentence summary]
|
|
796
|
+
- **Scope**: [What's in/out of scope]
|
|
797
|
+
- **Risk Level**: [Low/Medium/High]
|
|
798
|
+
|
|
799
|
+
### EXECUTION PHASES
|
|
800
|
+
|
|
801
|
+
#### Phase 1: [Name] (PARALLEL)
|
|
802
|
+
| Task | Agent | Files | Est |
|
|
803
|
+
|------|-------|-------|-----|
|
|
804
|
+
| [description] | explore | file.py | S/M/L |
|
|
805
|
+
|
|
806
|
+
#### Phase 2: [Name] (SEQUENTIAL after Phase 1)
|
|
807
|
+
| Task | Agent | Files | Est |
|
|
808
|
+
|------|-------|-------|-----|
|
|
809
|
+
|
|
810
|
+
### AGENT SPAWN COMMANDS
|
|
811
|
+
```python
|
|
812
|
+
# Phase 1 - Fire all in parallel
|
|
813
|
+
agent_spawn(prompt="...", agent_type="explore", description="...")
|
|
814
|
+
```
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
CONSTRAINTS:
|
|
818
|
+
- You ONLY plan. You NEVER execute code changes.
|
|
819
|
+
- Every task must have a clear agent assignment
|
|
820
|
+
- Parallel phases must be truly independent
|
|
821
|
+
- Include ready-to-use agent_spawn commands""",
|
|
822
|
+
"research-lead": """You coordinate research tasks by spawning explore and dewey agents in parallel.
|
|
823
|
+
|
|
824
|
+
## Your Role
|
|
825
|
+
1. Receive research objective from Stravinsky
|
|
826
|
+
2. Decompose into parallel search tasks
|
|
827
|
+
3. Spawn explore/dewey agents for each task
|
|
828
|
+
4. Collect and SYNTHESIZE results
|
|
829
|
+
5. Return structured findings (not raw outputs)
|
|
830
|
+
|
|
831
|
+
## Output Format
|
|
832
|
+
Always return a Research Brief:
|
|
833
|
+
```json
|
|
834
|
+
{
|
|
835
|
+
"objective": "Original research goal",
|
|
836
|
+
"findings": [
|
|
837
|
+
{"source": "agent_id", "summary": "Key finding", "confidence": "high/medium/low"},
|
|
838
|
+
...
|
|
839
|
+
],
|
|
840
|
+
"synthesis": "Combined analysis of all findings",
|
|
841
|
+
"gaps": ["Information we couldn't find"],
|
|
842
|
+
"recommendations": ["Suggested next steps"]
|
|
843
|
+
}
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
MODEL ROUTING:
|
|
847
|
+
Use invoke_gemini with model="gemini-3-flash" for ALL synthesis work.
|
|
848
|
+
""",
|
|
849
|
+
"implementation-lead": """You coordinate implementation based on research findings.
|
|
850
|
+
|
|
851
|
+
## Your Role
|
|
852
|
+
1. Receive Research Brief from Stravinsky
|
|
853
|
+
2. Create implementation plan
|
|
854
|
+
3. Delegate to specialists:
|
|
855
|
+
- frontend: UI/visual work
|
|
856
|
+
- debugger: Fix failures
|
|
857
|
+
- code-reviewer: Quality checks
|
|
858
|
+
4. Verify with lsp_diagnostics
|
|
859
|
+
5. Return Implementation Report
|
|
860
|
+
|
|
861
|
+
## Output Format
|
|
862
|
+
```json
|
|
863
|
+
{
|
|
864
|
+
"objective": "What was implemented",
|
|
865
|
+
"files_changed": ["path/to/file.py"],
|
|
866
|
+
"tests_status": "pass/fail/skipped",
|
|
867
|
+
"diagnostics": "clean/warnings/errors",
|
|
868
|
+
"blockers": ["Issues preventing completion"]
|
|
869
|
+
}
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
## Escalation Rules
|
|
873
|
+
- After 2 failed attempts → spawn debugger
|
|
874
|
+
- After debugger fails → escalate to Stravinsky with context
|
|
875
|
+
- NEVER call delphi directly
|
|
876
|
+
""",
|
|
672
877
|
}
|
|
673
878
|
|
|
674
879
|
system_prompt = system_prompts.get(agent_type, None)
|
|
675
880
|
|
|
676
|
-
#
|
|
677
|
-
#
|
|
678
|
-
#
|
|
679
|
-
#
|
|
680
|
-
#
|
|
681
|
-
# Agent model preferences (for reference - NOT passed to Claude CLI):
|
|
682
|
-
# - stravinsky: Claude Opus 4.5 (orchestration)
|
|
683
|
-
# - delphi: GPT-5.2 (strategic advice) - use invoke_openai
|
|
684
|
-
# - frontend: Gemini Pro High (UI/UX) - use invoke_gemini with thinking_budget
|
|
685
|
-
# - explore, dewey, document_writer, multimodal: Gemini Flash (fast) - use invoke_gemini
|
|
881
|
+
# Model routing (MANDATORY - enforced in system prompts):
|
|
882
|
+
# - explore, dewey, document_writer, multimodal → invoke_gemini(gemini-3-flash)
|
|
883
|
+
# - frontend → invoke_gemini(gemini-3-pro-high)
|
|
884
|
+
# - delphi → invoke_openai(gpt-5.2)
|
|
885
|
+
# - Unknown agent types (coding tasks) → Claude CLI --model sonnet
|
|
686
886
|
|
|
687
887
|
# Get token store for authentication
|
|
688
888
|
from ..auth.token_store import TokenStore
|
|
@@ -700,16 +900,20 @@ RESPONSE RULES:
|
|
|
700
900
|
timeout=timeout,
|
|
701
901
|
)
|
|
702
902
|
|
|
703
|
-
|
|
903
|
+
# Get display model and cost tier emoji for concise output
|
|
904
|
+
display_model = AGENT_DISPLAY_MODELS.get(agent_type, AGENT_DISPLAY_MODELS["_default"])
|
|
905
|
+
cost_emoji = get_agent_emoji(agent_type)
|
|
906
|
+
short_desc = (description or prompt[:50]).strip()
|
|
704
907
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
908
|
+
# If blocking mode (recommended for delphi), wait for completion
|
|
909
|
+
if blocking:
|
|
910
|
+
result = manager.get_output(task_id, block=True, timeout=timeout)
|
|
911
|
+
return f"{cost_emoji} {agent_type}:{display_model}('{short_desc}') [BLOCKING]\n\n{result}"
|
|
708
912
|
|
|
709
|
-
|
|
710
|
-
-
|
|
711
|
-
|
|
712
|
-
|
|
913
|
+
# Enhanced format: cost_emoji agent:model('description') status_emoji
|
|
914
|
+
# 🟢 explore:gemini-3-flash('Find auth...') ⏳
|
|
915
|
+
return f"""{cost_emoji} {agent_type}:{display_model}('{short_desc}') ⏳
|
|
916
|
+
task_id={task_id}"""
|
|
713
917
|
|
|
714
918
|
|
|
715
919
|
async def agent_output(task_id: str, block: bool = False) -> str:
|
|
@@ -799,7 +1003,7 @@ async def agent_list() -> str:
|
|
|
799
1003
|
if not tasks:
|
|
800
1004
|
return "No background agent tasks found."
|
|
801
1005
|
|
|
802
|
-
lines = [
|
|
1006
|
+
lines = []
|
|
803
1007
|
|
|
804
1008
|
for t in sorted(tasks, key=lambda x: x.get("created_at", ""), reverse=True):
|
|
805
1009
|
status_emoji = {
|
|
@@ -810,8 +1014,14 @@ async def agent_list() -> str:
|
|
|
810
1014
|
"cancelled": "⚠️",
|
|
811
1015
|
}.get(t["status"], "❓")
|
|
812
1016
|
|
|
1017
|
+
agent_type = t.get("agent_type", "unknown")
|
|
1018
|
+
display_model = AGENT_DISPLAY_MODELS.get(agent_type, AGENT_DISPLAY_MODELS["_default"])
|
|
1019
|
+
cost_emoji = get_agent_emoji(agent_type)
|
|
813
1020
|
desc = t.get("description", t.get("prompt", "")[:40])
|
|
814
|
-
|
|
1021
|
+
# Concise format: cost_emoji status agent:model('desc') id=xxx
|
|
1022
|
+
lines.append(
|
|
1023
|
+
f"{cost_emoji} {status_emoji} {agent_type}:{display_model}('{desc}') id={t['id']}"
|
|
1024
|
+
)
|
|
815
1025
|
|
|
816
1026
|
return "\n".join(lines)
|
|
817
1027
|
|
|
@@ -61,7 +61,8 @@ class BackgroundManager:
|
|
|
61
61
|
json.dump(tasks, f, indent=2)
|
|
62
62
|
|
|
63
63
|
def create_task(self, prompt: str, model: str) -> str:
|
|
64
|
-
|
|
64
|
+
import uuid as uuid_module # Local import for MCP context
|
|
65
|
+
task_id = str(uuid_module.uuid4())[:8]
|
|
65
66
|
task = BackgroundTask(
|
|
66
67
|
id=task_id,
|
|
67
68
|
prompt=prompt,
|