emdash-core 0.1.7__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.
- emdash_core/__init__.py +3 -0
- emdash_core/agent/__init__.py +37 -0
- emdash_core/agent/agents.py +225 -0
- emdash_core/agent/code_reviewer.py +476 -0
- emdash_core/agent/compaction.py +143 -0
- emdash_core/agent/context_manager.py +140 -0
- emdash_core/agent/events.py +338 -0
- emdash_core/agent/handlers.py +224 -0
- emdash_core/agent/inprocess_subagent.py +377 -0
- emdash_core/agent/mcp/__init__.py +50 -0
- emdash_core/agent/mcp/client.py +346 -0
- emdash_core/agent/mcp/config.py +302 -0
- emdash_core/agent/mcp/manager.py +496 -0
- emdash_core/agent/mcp/tool_factory.py +213 -0
- emdash_core/agent/prompts/__init__.py +38 -0
- emdash_core/agent/prompts/main_agent.py +104 -0
- emdash_core/agent/prompts/subagents.py +131 -0
- emdash_core/agent/prompts/workflow.py +136 -0
- emdash_core/agent/providers/__init__.py +34 -0
- emdash_core/agent/providers/base.py +143 -0
- emdash_core/agent/providers/factory.py +80 -0
- emdash_core/agent/providers/models.py +220 -0
- emdash_core/agent/providers/openai_provider.py +463 -0
- emdash_core/agent/providers/transformers_provider.py +217 -0
- emdash_core/agent/research/__init__.py +81 -0
- emdash_core/agent/research/agent.py +143 -0
- emdash_core/agent/research/controller.py +254 -0
- emdash_core/agent/research/critic.py +428 -0
- emdash_core/agent/research/macros.py +469 -0
- emdash_core/agent/research/planner.py +449 -0
- emdash_core/agent/research/researcher.py +436 -0
- emdash_core/agent/research/state.py +523 -0
- emdash_core/agent/research/synthesizer.py +594 -0
- emdash_core/agent/reviewer_profile.py +475 -0
- emdash_core/agent/rules.py +123 -0
- emdash_core/agent/runner.py +601 -0
- emdash_core/agent/session.py +262 -0
- emdash_core/agent/spec_schema.py +66 -0
- emdash_core/agent/specification.py +479 -0
- emdash_core/agent/subagent.py +397 -0
- emdash_core/agent/subagent_prompts.py +13 -0
- emdash_core/agent/toolkit.py +482 -0
- emdash_core/agent/toolkits/__init__.py +64 -0
- emdash_core/agent/toolkits/base.py +96 -0
- emdash_core/agent/toolkits/explore.py +47 -0
- emdash_core/agent/toolkits/plan.py +55 -0
- emdash_core/agent/tools/__init__.py +141 -0
- emdash_core/agent/tools/analytics.py +436 -0
- emdash_core/agent/tools/base.py +131 -0
- emdash_core/agent/tools/coding.py +484 -0
- emdash_core/agent/tools/github_mcp.py +592 -0
- emdash_core/agent/tools/history.py +13 -0
- emdash_core/agent/tools/modes.py +153 -0
- emdash_core/agent/tools/plan.py +206 -0
- emdash_core/agent/tools/plan_write.py +135 -0
- emdash_core/agent/tools/search.py +412 -0
- emdash_core/agent/tools/spec.py +341 -0
- emdash_core/agent/tools/task.py +262 -0
- emdash_core/agent/tools/task_output.py +204 -0
- emdash_core/agent/tools/tasks.py +454 -0
- emdash_core/agent/tools/traversal.py +588 -0
- emdash_core/agent/tools/web.py +179 -0
- emdash_core/analytics/__init__.py +5 -0
- emdash_core/analytics/engine.py +1286 -0
- emdash_core/api/__init__.py +5 -0
- emdash_core/api/agent.py +308 -0
- emdash_core/api/agents.py +154 -0
- emdash_core/api/analyze.py +264 -0
- emdash_core/api/auth.py +173 -0
- emdash_core/api/context.py +77 -0
- emdash_core/api/db.py +121 -0
- emdash_core/api/embed.py +131 -0
- emdash_core/api/feature.py +143 -0
- emdash_core/api/health.py +93 -0
- emdash_core/api/index.py +162 -0
- emdash_core/api/plan.py +110 -0
- emdash_core/api/projectmd.py +210 -0
- emdash_core/api/query.py +320 -0
- emdash_core/api/research.py +122 -0
- emdash_core/api/review.py +161 -0
- emdash_core/api/router.py +76 -0
- emdash_core/api/rules.py +116 -0
- emdash_core/api/search.py +119 -0
- emdash_core/api/spec.py +99 -0
- emdash_core/api/swarm.py +223 -0
- emdash_core/api/tasks.py +109 -0
- emdash_core/api/team.py +120 -0
- emdash_core/auth/__init__.py +17 -0
- emdash_core/auth/github.py +389 -0
- emdash_core/config.py +74 -0
- emdash_core/context/__init__.py +52 -0
- emdash_core/context/models.py +50 -0
- emdash_core/context/providers/__init__.py +11 -0
- emdash_core/context/providers/base.py +74 -0
- emdash_core/context/providers/explored_areas.py +183 -0
- emdash_core/context/providers/touched_areas.py +360 -0
- emdash_core/context/registry.py +73 -0
- emdash_core/context/reranker.py +199 -0
- emdash_core/context/service.py +260 -0
- emdash_core/context/session.py +352 -0
- emdash_core/core/__init__.py +104 -0
- emdash_core/core/config.py +454 -0
- emdash_core/core/exceptions.py +55 -0
- emdash_core/core/models.py +265 -0
- emdash_core/core/review_config.py +57 -0
- emdash_core/db/__init__.py +67 -0
- emdash_core/db/auth.py +134 -0
- emdash_core/db/models.py +91 -0
- emdash_core/db/provider.py +222 -0
- emdash_core/db/providers/__init__.py +5 -0
- emdash_core/db/providers/supabase.py +452 -0
- emdash_core/embeddings/__init__.py +24 -0
- emdash_core/embeddings/indexer.py +534 -0
- emdash_core/embeddings/models.py +192 -0
- emdash_core/embeddings/providers/__init__.py +7 -0
- emdash_core/embeddings/providers/base.py +112 -0
- emdash_core/embeddings/providers/fireworks.py +141 -0
- emdash_core/embeddings/providers/openai.py +104 -0
- emdash_core/embeddings/registry.py +146 -0
- emdash_core/embeddings/service.py +215 -0
- emdash_core/graph/__init__.py +26 -0
- emdash_core/graph/builder.py +134 -0
- emdash_core/graph/connection.py +692 -0
- emdash_core/graph/schema.py +416 -0
- emdash_core/graph/writer.py +667 -0
- emdash_core/ingestion/__init__.py +7 -0
- emdash_core/ingestion/change_detector.py +150 -0
- emdash_core/ingestion/git/__init__.py +5 -0
- emdash_core/ingestion/git/commit_analyzer.py +196 -0
- emdash_core/ingestion/github/__init__.py +6 -0
- emdash_core/ingestion/github/pr_fetcher.py +296 -0
- emdash_core/ingestion/github/task_extractor.py +100 -0
- emdash_core/ingestion/orchestrator.py +540 -0
- emdash_core/ingestion/parsers/__init__.py +10 -0
- emdash_core/ingestion/parsers/base_parser.py +66 -0
- emdash_core/ingestion/parsers/call_graph_builder.py +121 -0
- emdash_core/ingestion/parsers/class_extractor.py +154 -0
- emdash_core/ingestion/parsers/function_extractor.py +202 -0
- emdash_core/ingestion/parsers/import_analyzer.py +119 -0
- emdash_core/ingestion/parsers/python_parser.py +123 -0
- emdash_core/ingestion/parsers/registry.py +72 -0
- emdash_core/ingestion/parsers/ts_ast_parser.js +313 -0
- emdash_core/ingestion/parsers/typescript_parser.py +278 -0
- emdash_core/ingestion/repository.py +346 -0
- emdash_core/models/__init__.py +38 -0
- emdash_core/models/agent.py +68 -0
- emdash_core/models/index.py +77 -0
- emdash_core/models/query.py +113 -0
- emdash_core/planning/__init__.py +7 -0
- emdash_core/planning/agent_api.py +413 -0
- emdash_core/planning/context_builder.py +265 -0
- emdash_core/planning/feature_context.py +232 -0
- emdash_core/planning/feature_expander.py +646 -0
- emdash_core/planning/llm_explainer.py +198 -0
- emdash_core/planning/similarity.py +509 -0
- emdash_core/planning/team_focus.py +821 -0
- emdash_core/server.py +153 -0
- emdash_core/sse/__init__.py +5 -0
- emdash_core/sse/stream.py +196 -0
- emdash_core/swarm/__init__.py +17 -0
- emdash_core/swarm/merge_agent.py +383 -0
- emdash_core/swarm/session_manager.py +274 -0
- emdash_core/swarm/swarm_runner.py +226 -0
- emdash_core/swarm/task_definition.py +137 -0
- emdash_core/swarm/worker_spawner.py +319 -0
- emdash_core/swarm/worktree_manager.py +278 -0
- emdash_core/templates/__init__.py +10 -0
- emdash_core/templates/defaults/agent-builder.md.template +82 -0
- emdash_core/templates/defaults/focus.md.template +115 -0
- emdash_core/templates/defaults/pr-review-enhanced.md.template +309 -0
- emdash_core/templates/defaults/pr-review.md.template +80 -0
- emdash_core/templates/defaults/project.md.template +85 -0
- emdash_core/templates/defaults/research_critic.md.template +112 -0
- emdash_core/templates/defaults/research_planner.md.template +85 -0
- emdash_core/templates/defaults/research_synthesizer.md.template +128 -0
- emdash_core/templates/defaults/reviewer.md.template +81 -0
- emdash_core/templates/defaults/spec.md.template +41 -0
- emdash_core/templates/defaults/tasks.md.template +78 -0
- emdash_core/templates/loader.py +296 -0
- emdash_core/utils/__init__.py +45 -0
- emdash_core/utils/git.py +84 -0
- emdash_core/utils/image.py +502 -0
- emdash_core/utils/logger.py +51 -0
- emdash_core-0.1.7.dist-info/METADATA +35 -0
- emdash_core-0.1.7.dist-info/RECORD +187 -0
- emdash_core-0.1.7.dist-info/WHEEL +4 -0
- emdash_core-0.1.7.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
"""Planner agent for creating research plans.
|
|
2
|
+
|
|
3
|
+
The Planner decomposes research goals into questions that map to
|
|
4
|
+
how the team works. It aligns with team values by:
|
|
5
|
+
- V3: Creating reviewer-first output sections
|
|
6
|
+
- V4: Setting budgets for cost awareness
|
|
7
|
+
- V5: Ensuring actionable outcomes
|
|
8
|
+
- V6: Using team vocabulary
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
|
|
16
|
+
from ..providers import get_provider
|
|
17
|
+
from ..providers.factory import DEFAULT_MODEL
|
|
18
|
+
from .state import (
|
|
19
|
+
ResearchPlan,
|
|
20
|
+
ResearchQuestion,
|
|
21
|
+
)
|
|
22
|
+
from .macros import suggest_macros, TOOL_MACROS
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Valid tool names that can be suggested
|
|
26
|
+
VALID_TOOLS = set(TOOL_MACROS.keys())
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Standard questions aligned with team workflows
|
|
30
|
+
STANDARD_QUESTIONS = [
|
|
31
|
+
{
|
|
32
|
+
"category": "feature",
|
|
33
|
+
"template": "What is the feature/behavior of {topic}?",
|
|
34
|
+
"priority": "P0",
|
|
35
|
+
"deliverable": "Design",
|
|
36
|
+
"criteria": ["Entry points identified", "Main functionality described"],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"category": "implementation",
|
|
40
|
+
"template": "Where is {topic} implemented?",
|
|
41
|
+
"priority": "P0",
|
|
42
|
+
"deliverable": "Implementation",
|
|
43
|
+
"criteria": ["File paths found", "Key functions/classes identified"],
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"category": "dependencies",
|
|
47
|
+
"template": "What depends on {topic}?",
|
|
48
|
+
"priority": "P1",
|
|
49
|
+
"deliverable": "Implementation",
|
|
50
|
+
"criteria": ["Callers identified", "Dependency graph understood"],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"category": "ownership",
|
|
54
|
+
"template": "Who owns or touches {topic}?",
|
|
55
|
+
"priority": "P1",
|
|
56
|
+
"deliverable": "Review",
|
|
57
|
+
"criteria": ["Authors identified", "Expertise areas mapped"],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"category": "risk",
|
|
61
|
+
"template": "What's risky about {topic}?",
|
|
62
|
+
"priority": "P1",
|
|
63
|
+
"deliverable": "Review",
|
|
64
|
+
"criteria": ["Risk factors identified", "Impact scope assessed"],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"category": "testing",
|
|
68
|
+
"template": "What tests/CI validate {topic}?",
|
|
69
|
+
"priority": "P2",
|
|
70
|
+
"deliverable": "Testing",
|
|
71
|
+
"criteria": ["Test files identified", "Coverage understood"],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"category": "review",
|
|
75
|
+
"template": "What should a reviewer check for {topic}?",
|
|
76
|
+
"priority": "P1",
|
|
77
|
+
"deliverable": "Review",
|
|
78
|
+
"criteria": ["Review checklist created", "Critical paths identified"],
|
|
79
|
+
},
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
# Required sections in final report (V3: Reviewer-first)
|
|
83
|
+
REQUIRED_SECTIONS = [
|
|
84
|
+
"Findings (fact-grounded)",
|
|
85
|
+
"Evidence Coverage Matrix",
|
|
86
|
+
"Design/Spec Implications",
|
|
87
|
+
"Risks & Unknowns",
|
|
88
|
+
"Recommended Tasks",
|
|
89
|
+
"Reviewer Checklist",
|
|
90
|
+
"Tooling Summary",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
# Team values checklist for Critic
|
|
94
|
+
TEAM_VALUES_CHECKLIST = [
|
|
95
|
+
"V1: All claims have evidence IDs (no ungrounded statements)",
|
|
96
|
+
"V2: Evidence is reproducible (tool calls documented)",
|
|
97
|
+
"V3: Output includes reviewer checklist and acceptance criteria",
|
|
98
|
+
"V4: Budget was respected (no wasteful tool calls)",
|
|
99
|
+
"V5: Report ends with actionable tasks",
|
|
100
|
+
"V6: Uses team vocabulary (tasks, PRs, reviewers)",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
# Default budgets (V4: Cost awareness)
|
|
104
|
+
DEFAULT_BUDGETS = {
|
|
105
|
+
"tool_calls": 100,
|
|
106
|
+
"tokens": 150000,
|
|
107
|
+
"time_s": 600, # 10 minutes
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
PLANNER_SYSTEM_PROMPT = """You are a research planner that creates structured research plans.
|
|
112
|
+
|
|
113
|
+
Your job is to decompose a research goal into questions that align with team workflows.
|
|
114
|
+
|
|
115
|
+
TEAM VALUES YOU MUST RESPECT:
|
|
116
|
+
- V1: Truth over fluency - prefer "unknown" over guesses
|
|
117
|
+
- V2: Evidence-first - all claims must be backed by tool outputs
|
|
118
|
+
- V3: Reviewer-first - output must include review checklists
|
|
119
|
+
- V4: Cost awareness - minimize tool calls, start with cheap models
|
|
120
|
+
- V5: Actionable outcomes - end with concrete tasks
|
|
121
|
+
- V6: Team alignment - use team vocabulary
|
|
122
|
+
|
|
123
|
+
QUESTION CATEGORIES:
|
|
124
|
+
1. Feature/Behavior - What is it?
|
|
125
|
+
2. Implementation - Where is the code?
|
|
126
|
+
3. Dependencies - What depends on it?
|
|
127
|
+
4. Ownership - Who owns/touches it?
|
|
128
|
+
5. Risk - What could go wrong?
|
|
129
|
+
6. Testing - How is it tested?
|
|
130
|
+
7. Review - What should reviewers check?
|
|
131
|
+
|
|
132
|
+
AVAILABLE TOOL MACROS (only use these names):
|
|
133
|
+
- deep_feature_analysis: Understand feature behavior and impact
|
|
134
|
+
- team_activity_analysis: Find owners, expertise, velocity risks
|
|
135
|
+
- architectural_deep_dive: Map architecture and key modules
|
|
136
|
+
- implementation_trace: Trace specific implementation paths
|
|
137
|
+
- risk_assessment: Assess risks of modifications
|
|
138
|
+
- pr_context: Understand PR and change context
|
|
139
|
+
|
|
140
|
+
OUTPUT FORMAT:
|
|
141
|
+
Return a JSON object with:
|
|
142
|
+
{
|
|
143
|
+
"topic": "extracted topic from goal",
|
|
144
|
+
"questions": [
|
|
145
|
+
{
|
|
146
|
+
"qid": "Q1",
|
|
147
|
+
"question": "specific question",
|
|
148
|
+
"priority": "P0|P1|P2",
|
|
149
|
+
"success_criteria": ["criterion 1", "criterion 2"],
|
|
150
|
+
"suggested_tools": ["deep_feature_analysis"],
|
|
151
|
+
"deliverable": "Design|Implementation|Testing|Review|Ops"
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
"max_iterations": 5,
|
|
155
|
+
"budgets": {
|
|
156
|
+
"tool_calls": 100,
|
|
157
|
+
"tokens": 150000,
|
|
158
|
+
"time_s": 600
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
PRIORITIES:
|
|
163
|
+
- P0: Must answer (blocking)
|
|
164
|
+
- P1: Should answer (important)
|
|
165
|
+
- P2: Nice to have (optional)
|
|
166
|
+
|
|
167
|
+
Be specific in questions. Avoid vague language. Every question should be answerable with tools.
|
|
168
|
+
IMPORTANT: Only suggest tools from the AVAILABLE TOOL MACROS list above."""
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class PlannerAgent:
|
|
172
|
+
"""Creates research plans with team-aligned questions.
|
|
173
|
+
|
|
174
|
+
The Planner analyzes the research goal and creates a structured
|
|
175
|
+
plan with prioritized questions, budgets, and success criteria.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
def __init__(
|
|
179
|
+
self,
|
|
180
|
+
model: str = DEFAULT_MODEL,
|
|
181
|
+
verbose: bool = True,
|
|
182
|
+
):
|
|
183
|
+
"""Initialize the planner agent.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
model: LLM model to use
|
|
187
|
+
verbose: Whether to print progress
|
|
188
|
+
"""
|
|
189
|
+
self.provider = get_provider(model)
|
|
190
|
+
self.model = model
|
|
191
|
+
self.verbose = verbose
|
|
192
|
+
self.console = Console()
|
|
193
|
+
|
|
194
|
+
def create_plan(
|
|
195
|
+
self,
|
|
196
|
+
goal: str,
|
|
197
|
+
context: str = "",
|
|
198
|
+
max_iterations: int = 3,
|
|
199
|
+
budgets: Optional[dict] = None,
|
|
200
|
+
) -> ResearchPlan:
|
|
201
|
+
"""Create a research plan for a goal.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
goal: The research goal
|
|
205
|
+
context: Additional context
|
|
206
|
+
max_iterations: Maximum research iterations
|
|
207
|
+
budgets: Resource budgets (tool_calls, tokens, time_s)
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
ResearchPlan with questions and budgets
|
|
211
|
+
"""
|
|
212
|
+
if self.verbose:
|
|
213
|
+
self.console.print(f"[cyan]Planning research for:[/cyan] {goal}")
|
|
214
|
+
|
|
215
|
+
# Try LLM-based planning first
|
|
216
|
+
try:
|
|
217
|
+
plan = self._llm_plan(goal, context, max_iterations, budgets)
|
|
218
|
+
if plan and plan.questions:
|
|
219
|
+
if self.verbose:
|
|
220
|
+
self.console.print(f"[green]Created plan with {len(plan.questions)} questions[/green]")
|
|
221
|
+
return plan
|
|
222
|
+
except Exception as e:
|
|
223
|
+
if self.verbose:
|
|
224
|
+
self.console.print(f"[yellow]LLM planning failed: {e}. Using template.[/yellow]")
|
|
225
|
+
|
|
226
|
+
# Fallback to template-based planning
|
|
227
|
+
return self._template_plan(goal, context, max_iterations, budgets)
|
|
228
|
+
|
|
229
|
+
def _llm_plan(
|
|
230
|
+
self,
|
|
231
|
+
goal: str,
|
|
232
|
+
context: str,
|
|
233
|
+
max_iterations: int,
|
|
234
|
+
budgets: Optional[dict],
|
|
235
|
+
) -> Optional[ResearchPlan]:
|
|
236
|
+
"""Create plan using LLM.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
goal: Research goal
|
|
240
|
+
context: Additional context
|
|
241
|
+
max_iterations: Max iterations
|
|
242
|
+
budgets: Resource budgets
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
ResearchPlan or None on failure
|
|
246
|
+
"""
|
|
247
|
+
user_message = f"""Create a research plan for this goal:
|
|
248
|
+
|
|
249
|
+
GOAL: {goal}
|
|
250
|
+
|
|
251
|
+
{f'CONTEXT: {context}' if context else ''}
|
|
252
|
+
|
|
253
|
+
Create questions that will help understand this topic thoroughly.
|
|
254
|
+
Prioritize P0 questions that are essential to answer.
|
|
255
|
+
Suggest appropriate tool macros for each question.
|
|
256
|
+
|
|
257
|
+
Return JSON only, no markdown code blocks."""
|
|
258
|
+
|
|
259
|
+
messages = [
|
|
260
|
+
{"role": "user", "content": user_message},
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
response = self.provider.chat(messages, system=PLANNER_SYSTEM_PROMPT)
|
|
264
|
+
content = response.content or ""
|
|
265
|
+
|
|
266
|
+
# Parse JSON from response
|
|
267
|
+
try:
|
|
268
|
+
# Try to extract JSON from response
|
|
269
|
+
json_str = content
|
|
270
|
+
if "```" in content:
|
|
271
|
+
# Extract from code block
|
|
272
|
+
start = content.find("```")
|
|
273
|
+
end = content.find("```", start + 3)
|
|
274
|
+
if end > start:
|
|
275
|
+
json_str = content[start + 3:end]
|
|
276
|
+
if json_str.startswith("json"):
|
|
277
|
+
json_str = json_str[4:]
|
|
278
|
+
|
|
279
|
+
data = json.loads(json_str.strip())
|
|
280
|
+
|
|
281
|
+
# Build questions
|
|
282
|
+
questions = []
|
|
283
|
+
for i, q in enumerate(data.get("questions", [])):
|
|
284
|
+
# Filter suggested tools to only include valid ones
|
|
285
|
+
raw_tools = q.get("suggested_tools", [])
|
|
286
|
+
valid_suggested = [t for t in raw_tools if t in VALID_TOOLS]
|
|
287
|
+
# If no valid tools, use suggest_macros to pick appropriate ones
|
|
288
|
+
if not valid_suggested:
|
|
289
|
+
valid_suggested = suggest_macros(q.get("question", ""))
|
|
290
|
+
|
|
291
|
+
questions.append(ResearchQuestion(
|
|
292
|
+
qid=q.get("qid", f"Q{i+1}"),
|
|
293
|
+
question=q["question"],
|
|
294
|
+
priority=q.get("priority", "P1"),
|
|
295
|
+
success_criteria=q.get("success_criteria", []),
|
|
296
|
+
suggested_tools=valid_suggested,
|
|
297
|
+
deliverable=q.get("deliverable", "Implementation"),
|
|
298
|
+
))
|
|
299
|
+
|
|
300
|
+
if not questions:
|
|
301
|
+
return None
|
|
302
|
+
|
|
303
|
+
# Use provided budgets or defaults
|
|
304
|
+
final_budgets = budgets or data.get("budgets", DEFAULT_BUDGETS)
|
|
305
|
+
|
|
306
|
+
return ResearchPlan(
|
|
307
|
+
goal=goal,
|
|
308
|
+
questions=questions,
|
|
309
|
+
max_iterations=data.get("max_iterations", max_iterations),
|
|
310
|
+
budgets=final_budgets,
|
|
311
|
+
required_sections=REQUIRED_SECTIONS,
|
|
312
|
+
team_values_checklist=TEAM_VALUES_CHECKLIST,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
except (json.JSONDecodeError, KeyError, TypeError):
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
def _template_plan(
|
|
319
|
+
self,
|
|
320
|
+
goal: str,
|
|
321
|
+
context: str,
|
|
322
|
+
max_iterations: int,
|
|
323
|
+
budgets: Optional[dict],
|
|
324
|
+
) -> ResearchPlan:
|
|
325
|
+
"""Create plan using templates.
|
|
326
|
+
|
|
327
|
+
Fallback method that generates questions from standard templates.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
goal: Research goal
|
|
331
|
+
context: Additional context
|
|
332
|
+
max_iterations: Max iterations
|
|
333
|
+
budgets: Resource budgets
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
ResearchPlan
|
|
337
|
+
"""
|
|
338
|
+
# Extract topic from goal
|
|
339
|
+
topic = self._extract_topic(goal)
|
|
340
|
+
|
|
341
|
+
# Generate questions from templates
|
|
342
|
+
questions = []
|
|
343
|
+
suggested_macros = suggest_macros(goal)
|
|
344
|
+
|
|
345
|
+
for i, template in enumerate(STANDARD_QUESTIONS):
|
|
346
|
+
question_text = template["template"].format(topic=topic)
|
|
347
|
+
|
|
348
|
+
# Suggest macros based on category
|
|
349
|
+
tools = []
|
|
350
|
+
if template["category"] == "feature":
|
|
351
|
+
tools = ["deep_feature_analysis"]
|
|
352
|
+
elif template["category"] == "implementation":
|
|
353
|
+
tools = ["implementation_trace"]
|
|
354
|
+
elif template["category"] == "dependencies":
|
|
355
|
+
tools = ["deep_feature_analysis"]
|
|
356
|
+
elif template["category"] == "ownership":
|
|
357
|
+
tools = ["team_activity_analysis"]
|
|
358
|
+
elif template["category"] == "risk":
|
|
359
|
+
tools = ["risk_assessment"]
|
|
360
|
+
elif template["category"] == "testing":
|
|
361
|
+
tools = ["implementation_trace"]
|
|
362
|
+
elif template["category"] == "review":
|
|
363
|
+
tools = ["risk_assessment", "team_activity_analysis"]
|
|
364
|
+
|
|
365
|
+
questions.append(ResearchQuestion(
|
|
366
|
+
qid=f"Q{i+1}",
|
|
367
|
+
question=question_text,
|
|
368
|
+
priority=template["priority"],
|
|
369
|
+
success_criteria=template["criteria"],
|
|
370
|
+
suggested_tools=tools,
|
|
371
|
+
deliverable=template["deliverable"],
|
|
372
|
+
))
|
|
373
|
+
|
|
374
|
+
return ResearchPlan(
|
|
375
|
+
goal=goal,
|
|
376
|
+
questions=questions,
|
|
377
|
+
max_iterations=max_iterations,
|
|
378
|
+
budgets=budgets or DEFAULT_BUDGETS,
|
|
379
|
+
required_sections=REQUIRED_SECTIONS,
|
|
380
|
+
team_values_checklist=TEAM_VALUES_CHECKLIST,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def _extract_topic(self, goal: str) -> str:
|
|
384
|
+
"""Extract the main topic from a goal.
|
|
385
|
+
|
|
386
|
+
Simple heuristic extraction. Could be improved with NLP.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
goal: The research goal
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Extracted topic
|
|
393
|
+
"""
|
|
394
|
+
# Remove common prefixes
|
|
395
|
+
topic = goal
|
|
396
|
+
prefixes = [
|
|
397
|
+
"how does", "what is", "where is", "who owns",
|
|
398
|
+
"explain", "understand", "investigate", "research",
|
|
399
|
+
"analyze", "find", "look into",
|
|
400
|
+
]
|
|
401
|
+
|
|
402
|
+
topic_lower = topic.lower()
|
|
403
|
+
for prefix in prefixes:
|
|
404
|
+
if topic_lower.startswith(prefix):
|
|
405
|
+
topic = topic[len(prefix):].strip()
|
|
406
|
+
break
|
|
407
|
+
|
|
408
|
+
# Remove trailing punctuation
|
|
409
|
+
topic = topic.rstrip("?!.")
|
|
410
|
+
|
|
411
|
+
# Clean up "the" at start
|
|
412
|
+
if topic.lower().startswith("the "):
|
|
413
|
+
topic = topic[4:]
|
|
414
|
+
|
|
415
|
+
return topic.strip() or goal
|
|
416
|
+
|
|
417
|
+
def adjust_plan(
|
|
418
|
+
self,
|
|
419
|
+
plan: ResearchPlan,
|
|
420
|
+
critique_feedback: list[str],
|
|
421
|
+
drop_p2: bool = False,
|
|
422
|
+
) -> ResearchPlan:
|
|
423
|
+
"""Adjust plan based on critique feedback.
|
|
424
|
+
|
|
425
|
+
Called when budget is running low or Critic requests changes.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
plan: Current plan
|
|
429
|
+
critique_feedback: Feedback from Critic
|
|
430
|
+
drop_p2: Whether to drop P2 questions
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
Adjusted ResearchPlan
|
|
434
|
+
"""
|
|
435
|
+
questions = list(plan.questions)
|
|
436
|
+
|
|
437
|
+
if drop_p2:
|
|
438
|
+
questions = [q for q in questions if q.priority != "P2"]
|
|
439
|
+
|
|
440
|
+
# Could add more sophisticated adjustment based on feedback
|
|
441
|
+
|
|
442
|
+
return ResearchPlan(
|
|
443
|
+
goal=plan.goal,
|
|
444
|
+
questions=questions,
|
|
445
|
+
max_iterations=plan.max_iterations,
|
|
446
|
+
budgets=plan.budgets,
|
|
447
|
+
required_sections=plan.required_sections,
|
|
448
|
+
team_values_checklist=plan.team_values_checklist,
|
|
449
|
+
)
|