steerdev 0.4.27__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.
- steerdev-0.4.27.dist-info/METADATA +224 -0
- steerdev-0.4.27.dist-info/RECORD +57 -0
- steerdev-0.4.27.dist-info/WHEEL +4 -0
- steerdev-0.4.27.dist-info/entry_points.txt +2 -0
- steerdev_agent/__init__.py +10 -0
- steerdev_agent/api/__init__.py +32 -0
- steerdev_agent/api/activity.py +278 -0
- steerdev_agent/api/agents.py +145 -0
- steerdev_agent/api/client.py +158 -0
- steerdev_agent/api/commands.py +399 -0
- steerdev_agent/api/configs.py +238 -0
- steerdev_agent/api/context.py +306 -0
- steerdev_agent/api/events.py +294 -0
- steerdev_agent/api/hooks.py +178 -0
- steerdev_agent/api/implementation_plan.py +408 -0
- steerdev_agent/api/messages.py +231 -0
- steerdev_agent/api/prd.py +281 -0
- steerdev_agent/api/runs.py +526 -0
- steerdev_agent/api/sessions.py +403 -0
- steerdev_agent/api/specs.py +321 -0
- steerdev_agent/api/tasks.py +659 -0
- steerdev_agent/api/workflow_runs.py +351 -0
- steerdev_agent/api/workflows.py +191 -0
- steerdev_agent/cli.py +2254 -0
- steerdev_agent/config/__init__.py +19 -0
- steerdev_agent/config/models.py +236 -0
- steerdev_agent/config/platform.py +272 -0
- steerdev_agent/config/settings.py +62 -0
- steerdev_agent/daemon.py +675 -0
- steerdev_agent/executor/__init__.py +64 -0
- steerdev_agent/executor/base.py +121 -0
- steerdev_agent/executor/claude.py +328 -0
- steerdev_agent/executor/stream.py +163 -0
- steerdev_agent/git/__init__.py +1 -0
- steerdev_agent/handlers/__init__.py +5 -0
- steerdev_agent/handlers/prd.py +533 -0
- steerdev_agent/integration.py +334 -0
- steerdev_agent/prompt/__init__.py +10 -0
- steerdev_agent/prompt/builder.py +263 -0
- steerdev_agent/prompt/templates.py +422 -0
- steerdev_agent/py.typed +0 -0
- steerdev_agent/runner.py +829 -0
- steerdev_agent/setup/__init__.py +5 -0
- steerdev_agent/setup/claude_setup.py +560 -0
- steerdev_agent/setup/templates/claude_md_section.md +140 -0
- steerdev_agent/setup/templates/settings.json +69 -0
- steerdev_agent/setup/templates/skills/activity/SKILL.md +160 -0
- steerdev_agent/setup/templates/skills/context/SKILL.md +122 -0
- steerdev_agent/setup/templates/skills/git-workflow/SKILL.md +218 -0
- steerdev_agent/setup/templates/skills/progress-logging/SKILL.md +211 -0
- steerdev_agent/setup/templates/skills/specs-management/SKILL.md +161 -0
- steerdev_agent/setup/templates/skills/task-management/SKILL.md +343 -0
- steerdev_agent/setup/templates/steerdev.yaml +51 -0
- steerdev_agent/version.py +149 -0
- steerdev_agent/workflow/__init__.py +10 -0
- steerdev_agent/workflow/executor.py +494 -0
- steerdev_agent/workflow/memory.py +185 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
"""PRD message handler for agent-driven PRD analysis and task generation.
|
|
2
|
+
|
|
3
|
+
This handler processes messages from the SteerDev API related to PRD documents:
|
|
4
|
+
- prd_analyze: Analyze PRD and generate clarification questions
|
|
5
|
+
- prd_generate_plan: Generate implementation plan from clarified PRD
|
|
6
|
+
- prd_generate_tasks: Generate development tasks from implementation plan
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from loguru import logger
|
|
15
|
+
from pydantic import BaseModel
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
|
|
18
|
+
from steerdev_agent.api.client import get_agent_id
|
|
19
|
+
from steerdev_agent.api.prd import PRDClient
|
|
20
|
+
from steerdev_agent.api.tasks import TasksClient
|
|
21
|
+
from steerdev_agent.prompt.templates import PromptTemplates
|
|
22
|
+
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PRDAnalyzePayload(BaseModel):
|
|
27
|
+
"""Payload for prd_analyze message type."""
|
|
28
|
+
|
|
29
|
+
type: str = "prd_analyze"
|
|
30
|
+
prd_id: str
|
|
31
|
+
prd_content: str
|
|
32
|
+
prd_title: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PRDGeneratePlanPayload(BaseModel):
|
|
36
|
+
"""Payload for prd_generate_plan message type."""
|
|
37
|
+
|
|
38
|
+
type: str = "prd_generate_plan"
|
|
39
|
+
prd_id: str
|
|
40
|
+
prd_content: str
|
|
41
|
+
prd_title: str
|
|
42
|
+
clarifications: list[dict[str, str]] = []
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class PRDGenerateTasksPayload(BaseModel):
|
|
46
|
+
"""Payload for prd_generate_tasks message type."""
|
|
47
|
+
|
|
48
|
+
type: str = "prd_generate_tasks"
|
|
49
|
+
prd_id: str
|
|
50
|
+
project_id: str
|
|
51
|
+
prd_title: str
|
|
52
|
+
implementation_plan: dict[str, Any]
|
|
53
|
+
linear_team_id: str | None = None
|
|
54
|
+
linear_project_id: str | None = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class PRDHandlerResult(BaseModel):
|
|
58
|
+
"""Result of PRD handler operation."""
|
|
59
|
+
|
|
60
|
+
success: bool
|
|
61
|
+
message_type: str
|
|
62
|
+
prd_id: str
|
|
63
|
+
data: dict[str, Any] = {}
|
|
64
|
+
error: str | None = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class PRDHandler:
|
|
68
|
+
"""Handler for PRD-related agent messages.
|
|
69
|
+
|
|
70
|
+
This handler integrates with the SteerDev API to:
|
|
71
|
+
1. Analyze PRDs and post clarification questions
|
|
72
|
+
2. Generate implementation plans
|
|
73
|
+
3. Generate development tasks
|
|
74
|
+
|
|
75
|
+
It uses LLM (via prompts) for analysis and stores results via the API.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
api_key: str | None = None,
|
|
81
|
+
agent_id: str | None = None,
|
|
82
|
+
agent_name: str = "SteerDev Agent",
|
|
83
|
+
) -> None:
|
|
84
|
+
"""Initialize the PRD handler.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
api_key: API key for authentication.
|
|
88
|
+
agent_id: Agent ID for attribution.
|
|
89
|
+
agent_name: Display name for the agent.
|
|
90
|
+
"""
|
|
91
|
+
self.api_key = api_key
|
|
92
|
+
self.agent_id = agent_id or get_agent_id() or "unknown"
|
|
93
|
+
self.agent_name = agent_name
|
|
94
|
+
self._prd_client: PRDClient | None = None
|
|
95
|
+
self._tasks_client: TasksClient | None = None
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def prd_client(self) -> PRDClient:
|
|
99
|
+
"""Get or create PRD client."""
|
|
100
|
+
if self._prd_client is None:
|
|
101
|
+
self._prd_client = PRDClient(api_key=self.api_key)
|
|
102
|
+
return self._prd_client
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def tasks_client(self) -> TasksClient:
|
|
106
|
+
"""Get or create Tasks client."""
|
|
107
|
+
if self._tasks_client is None:
|
|
108
|
+
self._tasks_client = TasksClient(api_key=self.api_key)
|
|
109
|
+
return self._tasks_client
|
|
110
|
+
|
|
111
|
+
def handle_message(self, message_content: str) -> PRDHandlerResult:
|
|
112
|
+
"""Handle a PRD-related message.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
message_content: JSON string containing the message payload.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Handler result with success status and data.
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
payload = json.loads(message_content)
|
|
122
|
+
message_type = payload.get("type", "unknown")
|
|
123
|
+
|
|
124
|
+
if message_type == "prd_analyze":
|
|
125
|
+
return self._handle_analyze(PRDAnalyzePayload(**payload))
|
|
126
|
+
elif message_type == "prd_generate_plan":
|
|
127
|
+
return self._handle_generate_plan(PRDGeneratePlanPayload(**payload))
|
|
128
|
+
elif message_type == "prd_generate_tasks":
|
|
129
|
+
return self._handle_generate_tasks(PRDGenerateTasksPayload(**payload))
|
|
130
|
+
else:
|
|
131
|
+
return PRDHandlerResult(
|
|
132
|
+
success=False,
|
|
133
|
+
message_type=message_type,
|
|
134
|
+
prd_id=payload.get("prd_id", "unknown"),
|
|
135
|
+
error=f"Unknown message type: {message_type}",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
except json.JSONDecodeError as e:
|
|
139
|
+
logger.error(f"Failed to parse message content: {e}")
|
|
140
|
+
return PRDHandlerResult(
|
|
141
|
+
success=False,
|
|
142
|
+
message_type="unknown",
|
|
143
|
+
prd_id="unknown",
|
|
144
|
+
error=f"Invalid JSON: {e}",
|
|
145
|
+
)
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.exception(f"Error handling PRD message: {e}")
|
|
148
|
+
return PRDHandlerResult(
|
|
149
|
+
success=False,
|
|
150
|
+
message_type="unknown",
|
|
151
|
+
prd_id="unknown",
|
|
152
|
+
error=str(e),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def _handle_analyze(self, payload: PRDAnalyzePayload) -> PRDHandlerResult:
|
|
156
|
+
"""Handle PRD analysis - generate clarification questions.
|
|
157
|
+
|
|
158
|
+
This method:
|
|
159
|
+
1. Builds the analysis prompt
|
|
160
|
+
2. Returns the prompt for the agent to execute
|
|
161
|
+
3. The agent response should be parsed and questions posted via API
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
payload: Analysis payload with PRD content.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Result with prompt to execute.
|
|
168
|
+
"""
|
|
169
|
+
console.print(f"\n[bold cyan]Analyzing PRD: {payload.prd_title}[/bold cyan]")
|
|
170
|
+
|
|
171
|
+
# Build the analysis prompt
|
|
172
|
+
prompt = PromptTemplates.format_prd_analyze(
|
|
173
|
+
prd_title=payload.prd_title,
|
|
174
|
+
prd_content=payload.prd_content,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Return the prompt - the runner will execute this and call back with results
|
|
178
|
+
return PRDHandlerResult(
|
|
179
|
+
success=True,
|
|
180
|
+
message_type="prd_analyze",
|
|
181
|
+
prd_id=payload.prd_id,
|
|
182
|
+
data={
|
|
183
|
+
"prompt": prompt,
|
|
184
|
+
"next_action": "execute_and_parse",
|
|
185
|
+
"response_handler": "parse_analysis_response",
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def parse_analysis_response(
|
|
190
|
+
self,
|
|
191
|
+
prd_id: str,
|
|
192
|
+
response_text: str,
|
|
193
|
+
) -> PRDHandlerResult:
|
|
194
|
+
"""Parse LLM analysis response and post questions to API.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
prd_id: PRD document ID.
|
|
198
|
+
response_text: Raw LLM response text.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Result with posted questions data.
|
|
202
|
+
"""
|
|
203
|
+
try:
|
|
204
|
+
# Extract JSON from response (may be wrapped in markdown code block)
|
|
205
|
+
json_text = response_text
|
|
206
|
+
if "```json" in response_text:
|
|
207
|
+
start = response_text.find("```json") + 7
|
|
208
|
+
end = response_text.find("```", start)
|
|
209
|
+
json_text = response_text[start:end].strip()
|
|
210
|
+
elif "```" in response_text:
|
|
211
|
+
start = response_text.find("```") + 3
|
|
212
|
+
end = response_text.find("```", start)
|
|
213
|
+
json_text = response_text[start:end].strip()
|
|
214
|
+
|
|
215
|
+
data = json.loads(json_text)
|
|
216
|
+
questions = data.get("questions", [])
|
|
217
|
+
summary = data.get("summary", "")
|
|
218
|
+
observations = data.get("initial_observations", "")
|
|
219
|
+
|
|
220
|
+
# Post questions as comments
|
|
221
|
+
if questions:
|
|
222
|
+
posted_comments = self.prd_client.post_questions(
|
|
223
|
+
prd_id=prd_id,
|
|
224
|
+
questions=questions,
|
|
225
|
+
agent_id=self.agent_id,
|
|
226
|
+
agent_name=self.agent_name,
|
|
227
|
+
)
|
|
228
|
+
console.print(f"[green]Posted {len(posted_comments)} questions[/green]")
|
|
229
|
+
else:
|
|
230
|
+
console.print("[yellow]No questions to post[/yellow]")
|
|
231
|
+
|
|
232
|
+
# Post summary and observations as notes
|
|
233
|
+
if summary:
|
|
234
|
+
self.prd_client.post_note(
|
|
235
|
+
prd_id=prd_id,
|
|
236
|
+
content=f"**Summary:** {summary}",
|
|
237
|
+
agent_id=self.agent_id,
|
|
238
|
+
agent_name=self.agent_name,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
if observations:
|
|
242
|
+
self.prd_client.post_note(
|
|
243
|
+
prd_id=prd_id,
|
|
244
|
+
content=f"**Initial Observations:** {observations}",
|
|
245
|
+
agent_id=self.agent_id,
|
|
246
|
+
agent_name=self.agent_name,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# Update PRD status to clarifying
|
|
250
|
+
self.prd_client.update_prd_status(prd_id, "clarifying")
|
|
251
|
+
|
|
252
|
+
return PRDHandlerResult(
|
|
253
|
+
success=True,
|
|
254
|
+
message_type="prd_analyze",
|
|
255
|
+
prd_id=prd_id,
|
|
256
|
+
data={
|
|
257
|
+
"questions_count": len(questions),
|
|
258
|
+
"summary": summary,
|
|
259
|
+
},
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
except json.JSONDecodeError as e:
|
|
263
|
+
logger.error(f"Failed to parse analysis response: {e}")
|
|
264
|
+
# Try to recover - update status anyway
|
|
265
|
+
self.prd_client.update_prd_status(prd_id, "clarifying")
|
|
266
|
+
return PRDHandlerResult(
|
|
267
|
+
success=False,
|
|
268
|
+
message_type="prd_analyze",
|
|
269
|
+
prd_id=prd_id,
|
|
270
|
+
error=f"Failed to parse response: {e}",
|
|
271
|
+
)
|
|
272
|
+
except Exception as e:
|
|
273
|
+
logger.exception(f"Error parsing analysis response: {e}")
|
|
274
|
+
return PRDHandlerResult(
|
|
275
|
+
success=False,
|
|
276
|
+
message_type="prd_analyze",
|
|
277
|
+
prd_id=prd_id,
|
|
278
|
+
error=str(e),
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
def _handle_generate_plan(self, payload: PRDGeneratePlanPayload) -> PRDHandlerResult:
|
|
282
|
+
"""Handle implementation plan generation.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
payload: Plan generation payload with PRD content and clarifications.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Result with prompt to execute.
|
|
289
|
+
"""
|
|
290
|
+
console.print(f"\n[bold cyan]Generating plan for: {payload.prd_title}[/bold cyan]")
|
|
291
|
+
|
|
292
|
+
# Build the plan generation prompt
|
|
293
|
+
prompt = PromptTemplates.format_prd_generate_plan(
|
|
294
|
+
prd_title=payload.prd_title,
|
|
295
|
+
prd_content=payload.prd_content,
|
|
296
|
+
clarifications=payload.clarifications,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
return PRDHandlerResult(
|
|
300
|
+
success=True,
|
|
301
|
+
message_type="prd_generate_plan",
|
|
302
|
+
prd_id=payload.prd_id,
|
|
303
|
+
data={
|
|
304
|
+
"prompt": prompt,
|
|
305
|
+
"next_action": "execute_and_parse",
|
|
306
|
+
"response_handler": "parse_plan_response",
|
|
307
|
+
},
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def parse_plan_response(
|
|
311
|
+
self,
|
|
312
|
+
prd_id: str,
|
|
313
|
+
response_text: str,
|
|
314
|
+
) -> PRDHandlerResult:
|
|
315
|
+
"""Parse LLM plan response and save to API.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
prd_id: PRD document ID.
|
|
319
|
+
response_text: Raw LLM response text.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
Result with saved plan data.
|
|
323
|
+
"""
|
|
324
|
+
try:
|
|
325
|
+
# Extract JSON from response
|
|
326
|
+
json_text = response_text
|
|
327
|
+
if "```json" in response_text:
|
|
328
|
+
start = response_text.find("```json") + 7
|
|
329
|
+
end = response_text.find("```", start)
|
|
330
|
+
json_text = response_text[start:end].strip()
|
|
331
|
+
elif "```" in response_text:
|
|
332
|
+
start = response_text.find("```") + 3
|
|
333
|
+
end = response_text.find("```", start)
|
|
334
|
+
json_text = response_text[start:end].strip()
|
|
335
|
+
|
|
336
|
+
plan = json.loads(json_text)
|
|
337
|
+
|
|
338
|
+
# Save the implementation plan
|
|
339
|
+
updated_prd = self.prd_client.save_implementation_plan(prd_id, plan)
|
|
340
|
+
|
|
341
|
+
if updated_prd:
|
|
342
|
+
console.print("[green]Implementation plan saved[/green]")
|
|
343
|
+
sections_count = len(plan.get("sections", []))
|
|
344
|
+
console.print(f"[dim]Sections: {sections_count}[/dim]")
|
|
345
|
+
|
|
346
|
+
return PRDHandlerResult(
|
|
347
|
+
success=True,
|
|
348
|
+
message_type="prd_generate_plan",
|
|
349
|
+
prd_id=prd_id,
|
|
350
|
+
data={
|
|
351
|
+
"plan": plan,
|
|
352
|
+
"sections_count": sections_count,
|
|
353
|
+
},
|
|
354
|
+
)
|
|
355
|
+
else:
|
|
356
|
+
return PRDHandlerResult(
|
|
357
|
+
success=False,
|
|
358
|
+
message_type="prd_generate_plan",
|
|
359
|
+
prd_id=prd_id,
|
|
360
|
+
error="Failed to save implementation plan",
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
except json.JSONDecodeError as e:
|
|
364
|
+
logger.error(f"Failed to parse plan response: {e}")
|
|
365
|
+
return PRDHandlerResult(
|
|
366
|
+
success=False,
|
|
367
|
+
message_type="prd_generate_plan",
|
|
368
|
+
prd_id=prd_id,
|
|
369
|
+
error=f"Failed to parse response: {e}",
|
|
370
|
+
)
|
|
371
|
+
except Exception as e:
|
|
372
|
+
logger.exception(f"Error parsing plan response: {e}")
|
|
373
|
+
return PRDHandlerResult(
|
|
374
|
+
success=False,
|
|
375
|
+
message_type="prd_generate_plan",
|
|
376
|
+
prd_id=prd_id,
|
|
377
|
+
error=str(e),
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
def _handle_generate_tasks(self, payload: PRDGenerateTasksPayload) -> PRDHandlerResult:
|
|
381
|
+
"""Handle task generation from implementation plan.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
payload: Task generation payload with implementation plan.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
Result with prompt to execute.
|
|
388
|
+
"""
|
|
389
|
+
console.print(f"\n[bold cyan]Generating tasks for: {payload.prd_title}[/bold cyan]")
|
|
390
|
+
|
|
391
|
+
# Build the task generation prompt
|
|
392
|
+
prompt = PromptTemplates.format_prd_generate_tasks(
|
|
393
|
+
prd_title=payload.prd_title,
|
|
394
|
+
implementation_plan=payload.implementation_plan,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
return PRDHandlerResult(
|
|
398
|
+
success=True,
|
|
399
|
+
message_type="prd_generate_tasks",
|
|
400
|
+
prd_id=payload.prd_id,
|
|
401
|
+
data={
|
|
402
|
+
"prompt": prompt,
|
|
403
|
+
"next_action": "execute_and_parse",
|
|
404
|
+
"response_handler": "parse_tasks_response",
|
|
405
|
+
"project_id": payload.project_id,
|
|
406
|
+
"linear_team_id": payload.linear_team_id,
|
|
407
|
+
"linear_project_id": payload.linear_project_id,
|
|
408
|
+
},
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
def parse_tasks_response(
|
|
412
|
+
self,
|
|
413
|
+
prd_id: str,
|
|
414
|
+
response_text: str,
|
|
415
|
+
project_id: str,
|
|
416
|
+
linear_team_id: str | None = None,
|
|
417
|
+
linear_project_id: str | None = None,
|
|
418
|
+
) -> PRDHandlerResult:
|
|
419
|
+
"""Parse LLM tasks response and create tasks via API.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
prd_id: PRD document ID.
|
|
423
|
+
response_text: Raw LLM response text.
|
|
424
|
+
project_id: Project ID to create tasks in.
|
|
425
|
+
linear_team_id: Optional Linear team ID.
|
|
426
|
+
linear_project_id: Optional Linear project ID.
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
Result with created tasks data.
|
|
430
|
+
"""
|
|
431
|
+
try:
|
|
432
|
+
# Extract JSON from response
|
|
433
|
+
json_text = response_text
|
|
434
|
+
if "```json" in response_text:
|
|
435
|
+
start = response_text.find("```json") + 7
|
|
436
|
+
end = response_text.find("```", start)
|
|
437
|
+
json_text = response_text[start:end].strip()
|
|
438
|
+
elif "```" in response_text:
|
|
439
|
+
start = response_text.find("```") + 3
|
|
440
|
+
end = response_text.find("```", start)
|
|
441
|
+
json_text = response_text[start:end].strip()
|
|
442
|
+
|
|
443
|
+
data = json.loads(json_text)
|
|
444
|
+
tasks = data.get("tasks", [])
|
|
445
|
+
|
|
446
|
+
if not tasks:
|
|
447
|
+
return PRDHandlerResult(
|
|
448
|
+
success=True,
|
|
449
|
+
message_type="prd_generate_tasks",
|
|
450
|
+
prd_id=prd_id,
|
|
451
|
+
data={"tasks_count": 0, "message": "No tasks to create"},
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
# Create tasks via API
|
|
455
|
+
created_count = 0
|
|
456
|
+
failed_count = 0
|
|
457
|
+
created_task_ids: list[str] = []
|
|
458
|
+
|
|
459
|
+
for task in tasks:
|
|
460
|
+
try:
|
|
461
|
+
created_task = self.tasks_client.create_task(
|
|
462
|
+
project_id=project_id,
|
|
463
|
+
title=task.get("title", "Untitled Task"),
|
|
464
|
+
prompt=task.get("description", ""),
|
|
465
|
+
priority=task.get("priority", 3),
|
|
466
|
+
source="prd",
|
|
467
|
+
metadata={
|
|
468
|
+
"prd_id": prd_id,
|
|
469
|
+
"section": task.get("section"),
|
|
470
|
+
"dependencies": task.get("dependencies", []),
|
|
471
|
+
},
|
|
472
|
+
)
|
|
473
|
+
if created_task:
|
|
474
|
+
created_count += 1
|
|
475
|
+
created_task_ids.append(created_task.get("id", ""))
|
|
476
|
+
else:
|
|
477
|
+
failed_count += 1
|
|
478
|
+
|
|
479
|
+
except Exception as e:
|
|
480
|
+
logger.error(f"Failed to create task: {e}")
|
|
481
|
+
failed_count += 1
|
|
482
|
+
|
|
483
|
+
console.print(f"[green]Created {created_count} tasks[/green]")
|
|
484
|
+
if failed_count > 0:
|
|
485
|
+
console.print(f"[yellow]Failed to create {failed_count} tasks[/yellow]")
|
|
486
|
+
|
|
487
|
+
# Update PRD status to completed
|
|
488
|
+
self.prd_client.update_prd_status(prd_id, "completed")
|
|
489
|
+
|
|
490
|
+
return PRDHandlerResult(
|
|
491
|
+
success=True,
|
|
492
|
+
message_type="prd_generate_tasks",
|
|
493
|
+
prd_id=prd_id,
|
|
494
|
+
data={
|
|
495
|
+
"tasks_count": created_count,
|
|
496
|
+
"failed_count": failed_count,
|
|
497
|
+
"task_ids": created_task_ids,
|
|
498
|
+
},
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
except json.JSONDecodeError as e:
|
|
502
|
+
logger.error(f"Failed to parse tasks response: {e}")
|
|
503
|
+
return PRDHandlerResult(
|
|
504
|
+
success=False,
|
|
505
|
+
message_type="prd_generate_tasks",
|
|
506
|
+
prd_id=prd_id,
|
|
507
|
+
error=f"Failed to parse response: {e}",
|
|
508
|
+
)
|
|
509
|
+
except Exception as e:
|
|
510
|
+
logger.exception(f"Error parsing tasks response: {e}")
|
|
511
|
+
return PRDHandlerResult(
|
|
512
|
+
success=False,
|
|
513
|
+
message_type="prd_generate_tasks",
|
|
514
|
+
prd_id=prd_id,
|
|
515
|
+
error=str(e),
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
def close(self) -> None:
|
|
519
|
+
"""Close client connections."""
|
|
520
|
+
if self._prd_client:
|
|
521
|
+
self._prd_client.close()
|
|
522
|
+
self._prd_client = None
|
|
523
|
+
if self._tasks_client:
|
|
524
|
+
self._tasks_client.close()
|
|
525
|
+
self._tasks_client = None
|
|
526
|
+
|
|
527
|
+
def __enter__(self) -> PRDHandler:
|
|
528
|
+
"""Enter context manager."""
|
|
529
|
+
return self
|
|
530
|
+
|
|
531
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
532
|
+
"""Exit context manager."""
|
|
533
|
+
self.close()
|