emdash-core 0.1.7__py3-none-any.whl → 0.1.33__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.
Files changed (55) hide show
  1. emdash_core/__init__.py +6 -1
  2. emdash_core/agent/__init__.py +4 -0
  3. emdash_core/agent/events.py +52 -1
  4. emdash_core/agent/inprocess_subagent.py +123 -10
  5. emdash_core/agent/prompts/__init__.py +6 -0
  6. emdash_core/agent/prompts/main_agent.py +53 -3
  7. emdash_core/agent/prompts/plan_mode.py +255 -0
  8. emdash_core/agent/prompts/subagents.py +84 -16
  9. emdash_core/agent/prompts/workflow.py +270 -56
  10. emdash_core/agent/providers/base.py +4 -0
  11. emdash_core/agent/providers/factory.py +2 -2
  12. emdash_core/agent/providers/models.py +7 -0
  13. emdash_core/agent/providers/openai_provider.py +137 -13
  14. emdash_core/agent/runner/__init__.py +49 -0
  15. emdash_core/agent/runner/agent_runner.py +753 -0
  16. emdash_core/agent/runner/context.py +451 -0
  17. emdash_core/agent/runner/factory.py +108 -0
  18. emdash_core/agent/runner/plan.py +217 -0
  19. emdash_core/agent/runner/sdk_runner.py +324 -0
  20. emdash_core/agent/runner/utils.py +67 -0
  21. emdash_core/agent/skills.py +358 -0
  22. emdash_core/agent/toolkit.py +85 -5
  23. emdash_core/agent/toolkits/plan.py +9 -11
  24. emdash_core/agent/tools/__init__.py +3 -2
  25. emdash_core/agent/tools/coding.py +48 -4
  26. emdash_core/agent/tools/modes.py +207 -55
  27. emdash_core/agent/tools/search.py +4 -0
  28. emdash_core/agent/tools/skill.py +193 -0
  29. emdash_core/agent/tools/spec.py +61 -94
  30. emdash_core/agent/tools/task.py +41 -2
  31. emdash_core/agent/tools/tasks.py +15 -78
  32. emdash_core/api/agent.py +562 -8
  33. emdash_core/api/index.py +1 -1
  34. emdash_core/api/projectmd.py +4 -2
  35. emdash_core/api/router.py +2 -0
  36. emdash_core/api/skills.py +241 -0
  37. emdash_core/checkpoint/__init__.py +40 -0
  38. emdash_core/checkpoint/cli.py +175 -0
  39. emdash_core/checkpoint/git_operations.py +250 -0
  40. emdash_core/checkpoint/manager.py +231 -0
  41. emdash_core/checkpoint/models.py +107 -0
  42. emdash_core/checkpoint/storage.py +201 -0
  43. emdash_core/config.py +1 -1
  44. emdash_core/core/config.py +18 -2
  45. emdash_core/graph/schema.py +5 -5
  46. emdash_core/ingestion/orchestrator.py +19 -10
  47. emdash_core/models/agent.py +1 -1
  48. emdash_core/server.py +42 -0
  49. emdash_core/skills/frontend-design/SKILL.md +56 -0
  50. emdash_core/sse/stream.py +5 -0
  51. {emdash_core-0.1.7.dist-info → emdash_core-0.1.33.dist-info}/METADATA +2 -2
  52. {emdash_core-0.1.7.dist-info → emdash_core-0.1.33.dist-info}/RECORD +54 -37
  53. {emdash_core-0.1.7.dist-info → emdash_core-0.1.33.dist-info}/entry_points.txt +1 -0
  54. emdash_core/agent/runner.py +0 -601
  55. {emdash_core-0.1.7.dist-info → emdash_core-0.1.33.dist-info}/WHEEL +0 -0
@@ -77,23 +77,41 @@ Creates a structured spec document with requirements and acceptance criteria."""
77
77
  ToolResult with spec info
78
78
  """
79
79
  try:
80
- spec = Spec(
81
- title=title,
82
- summary=summary,
83
- requirements=requirements,
84
- acceptance_criteria=acceptance_criteria,
85
- technical_notes=technical_notes or [],
86
- dependencies=dependencies or [],
87
- open_questions=open_questions or [],
88
- )
80
+ # Build markdown content from structured fields
81
+ content_parts = []
82
+
83
+ if summary:
84
+ content_parts.append(f"> {summary}\n")
85
+
86
+ if requirements:
87
+ content_parts.append("## Requirements")
88
+ content_parts.extend(f"- {req}" for req in requirements)
89
+ content_parts.append("")
90
+
91
+ if acceptance_criteria:
92
+ content_parts.append("## Acceptance Criteria")
93
+ content_parts.extend(f"- {crit}" for crit in acceptance_criteria)
94
+ content_parts.append("")
95
+
96
+ if technical_notes:
97
+ content_parts.append("## Technical Notes")
98
+ content_parts.extend(f"- {note}" for note in technical_notes)
99
+ content_parts.append("")
100
+
101
+ if dependencies:
102
+ content_parts.append("## Dependencies")
103
+ content_parts.extend(f"- {dep}" for dep in dependencies)
104
+ content_parts.append("")
105
+
106
+ if open_questions:
107
+ content_parts.append("## Open Questions")
108
+ content_parts.extend(f"- {q}" for q in open_questions)
109
+ content_parts.append("")
89
110
 
90
- # Validate
91
- errors = spec.validate()
92
- if errors:
93
- return ToolResult.error_result(
94
- f"Spec validation failed: {', '.join(errors)}",
95
- suggestions=["Ensure all required fields are provided"],
96
- )
111
+ content = "\n".join(content_parts)
112
+
113
+ # Create spec with title and content only (matches Spec schema)
114
+ spec = Spec(title=title, content=content)
97
115
 
98
116
  # Store in state
99
117
  state = SpecState.get_instance()
@@ -195,14 +213,8 @@ Returns the spec in markdown format."""
195
213
  return ToolResult.success_result(
196
214
  data={
197
215
  "title": spec.title,
198
- "summary": spec.summary,
199
- "requirements": spec.requirements,
200
- "acceptance_criteria": spec.acceptance_criteria,
201
- "technical_notes": spec.technical_notes,
202
- "dependencies": spec.dependencies,
203
- "open_questions": spec.open_questions,
216
+ "content": spec.content,
204
217
  "markdown": spec.to_markdown(),
205
- "is_complete": spec.is_complete(),
206
218
  },
207
219
  )
208
220
 
@@ -216,7 +228,7 @@ class UpdateSpecTool(BaseTool):
216
228
 
217
229
  name = "update_spec"
218
230
  description = """Update the current feature specification.
219
- Add or modify requirements, criteria, or other fields."""
231
+ Append content or replace the entire spec content."""
220
232
  category = ToolCategory.PLANNING
221
233
 
222
234
  def __init__(self, connection=None):
@@ -225,24 +237,17 @@ Add or modify requirements, criteria, or other fields."""
225
237
 
226
238
  def execute(
227
239
  self,
228
- add_requirements: Optional[list[str]] = None,
229
- add_acceptance_criteria: Optional[list[str]] = None,
230
- add_technical_notes: Optional[list[str]] = None,
231
- add_dependencies: Optional[list[str]] = None,
232
- add_open_questions: Optional[list[str]] = None,
233
- resolve_questions: Optional[list[str]] = None,
234
- update_summary: Optional[str] = None,
240
+ append_content: Optional[str] = None,
241
+ replace_content: Optional[str] = None,
242
+ update_title: Optional[str] = None,
243
+ **kwargs,
235
244
  ) -> ToolResult:
236
245
  """Update the current spec.
237
246
 
238
247
  Args:
239
- add_requirements: Requirements to add
240
- add_acceptance_criteria: Criteria to add
241
- add_technical_notes: Notes to add
242
- add_dependencies: Dependencies to add
243
- add_open_questions: Questions to add
244
- resolve_questions: Questions to mark as resolved
245
- update_summary: New summary text
248
+ append_content: Content to append to existing spec
249
+ replace_content: Content to replace entire spec content
250
+ update_title: New title for the spec
246
251
 
247
252
  Returns:
248
253
  ToolResult with updated spec
@@ -257,28 +262,15 @@ Add or modify requirements, criteria, or other fields."""
257
262
 
258
263
  spec = state.current_spec
259
264
 
260
- # Add new items
261
- if add_requirements:
262
- spec.requirements.extend(add_requirements)
263
- if add_acceptance_criteria:
264
- spec.acceptance_criteria.extend(add_acceptance_criteria)
265
- if add_technical_notes:
266
- spec.technical_notes.extend(add_technical_notes)
267
- if add_dependencies:
268
- spec.dependencies.extend(add_dependencies)
269
- if add_open_questions:
270
- spec.open_questions.extend(add_open_questions)
271
-
272
- # Resolve questions
273
- if resolve_questions:
274
- spec.open_questions = [
275
- q for q in spec.open_questions
276
- if q not in resolve_questions
277
- ]
278
-
279
- # Update summary
280
- if update_summary:
281
- spec.summary = update_summary
265
+ # Update title if provided
266
+ if update_title:
267
+ spec.title = update_title
268
+
269
+ # Replace or append content
270
+ if replace_content is not None:
271
+ spec.content = replace_content
272
+ elif append_content:
273
+ spec.content = spec.content + "\n\n" + append_content
282
274
 
283
275
  # Save if configured
284
276
  if state.save_path:
@@ -290,10 +282,7 @@ Add or modify requirements, criteria, or other fields."""
290
282
  return ToolResult.success_result(
291
283
  data={
292
284
  "title": spec.title,
293
- "requirements_count": len(spec.requirements),
294
- "acceptance_criteria_count": len(spec.acceptance_criteria),
295
- "open_questions_count": len(spec.open_questions),
296
- "is_complete": spec.is_complete(),
285
+ "content": spec.content,
297
286
  "markdown": spec.to_markdown(),
298
287
  },
299
288
  )
@@ -302,39 +291,17 @@ Add or modify requirements, criteria, or other fields."""
302
291
  """Get OpenAI function schema."""
303
292
  return self._make_schema(
304
293
  properties={
305
- "add_requirements": {
306
- "type": "array",
307
- "items": {"type": "string"},
308
- "description": "Requirements to add",
309
- },
310
- "add_acceptance_criteria": {
311
- "type": "array",
312
- "items": {"type": "string"},
313
- "description": "Acceptance criteria to add",
314
- },
315
- "add_technical_notes": {
316
- "type": "array",
317
- "items": {"type": "string"},
318
- "description": "Technical notes to add",
319
- },
320
- "add_dependencies": {
321
- "type": "array",
322
- "items": {"type": "string"},
323
- "description": "Dependencies to add",
324
- },
325
- "add_open_questions": {
326
- "type": "array",
327
- "items": {"type": "string"},
328
- "description": "Open questions to add",
294
+ "append_content": {
295
+ "type": "string",
296
+ "description": "Markdown content to append to existing spec",
329
297
  },
330
- "resolve_questions": {
331
- "type": "array",
332
- "items": {"type": "string"},
333
- "description": "Questions to mark as resolved",
298
+ "replace_content": {
299
+ "type": "string",
300
+ "description": "Markdown content to replace entire spec content",
334
301
  },
335
- "update_summary": {
302
+ "update_title": {
336
303
  "type": "string",
337
- "description": "New summary text",
304
+ "description": "New title for the spec",
338
305
  },
339
306
  },
340
307
  required=[],
@@ -62,6 +62,7 @@ Multiple sub-agents can be launched in parallel."""
62
62
  max_turns: int = 10,
63
63
  run_in_background: bool = False,
64
64
  resume: Optional[str] = None,
65
+ thoroughness: str = "medium",
65
66
  **kwargs,
66
67
  ) -> ToolResult:
67
68
  """Spawn a sub-agent to perform a task.
@@ -74,6 +75,7 @@ Multiple sub-agents can be launched in parallel."""
74
75
  max_turns: Maximum API round-trips
75
76
  run_in_background: Run asynchronously
76
77
  resume: Agent ID to resume from
78
+ thoroughness: Search thoroughness level (quick, medium, thorough)
77
79
 
78
80
  Returns:
79
81
  ToolResult with agent results or background task info
@@ -92,6 +94,11 @@ Multiple sub-agents can be launched in parallel."""
92
94
  suggestions=[f"Available types: {available_types}"],
93
95
  )
94
96
 
97
+ # Log current mode for debugging
98
+ from .modes import ModeState
99
+ mode_state = ModeState.get_instance()
100
+ log.info(f"TaskTool: current_mode={mode_state.current_mode}, subagent_type={subagent_type}")
101
+
95
102
  log.info(
96
103
  "Spawning sub-agent type={} model={} prompt={}",
97
104
  subagent_type,
@@ -99,16 +106,26 @@ Multiple sub-agents can be launched in parallel."""
99
106
  prompt[:50] + "..." if len(prompt) > 50 else prompt,
100
107
  )
101
108
 
109
+ # Emit subagent start event for UI visibility
110
+ if self.emitter:
111
+ from ..events import EventType
112
+ self.emitter.emit(EventType.SUBAGENT_START, {
113
+ "agent_type": subagent_type,
114
+ "prompt": prompt[:100] + "..." if len(prompt) > 100 else prompt,
115
+ "description": description,
116
+ })
117
+
102
118
  if run_in_background:
103
- return self._run_background(subagent_type, prompt, max_turns)
119
+ return self._run_background(subagent_type, prompt, max_turns, thoroughness)
104
120
  else:
105
- return self._run_sync(subagent_type, prompt, max_turns)
121
+ return self._run_sync(subagent_type, prompt, max_turns, thoroughness)
106
122
 
107
123
  def _run_sync(
108
124
  self,
109
125
  subagent_type: str,
110
126
  prompt: str,
111
127
  max_turns: int,
128
+ thoroughness: str = "medium",
112
129
  ) -> ToolResult:
113
130
  """Run sub-agent synchronously in the same process.
114
131
 
@@ -116,6 +133,7 @@ Multiple sub-agents can be launched in parallel."""
116
133
  subagent_type: Agent type
117
134
  prompt: Task prompt
118
135
  max_turns: Maximum API round-trips
136
+ thoroughness: Search thoroughness level
119
137
 
120
138
  Returns:
121
139
  ToolResult with agent results
@@ -127,8 +145,20 @@ Multiple sub-agents can be launched in parallel."""
127
145
  repo_root=self.repo_root,
128
146
  emitter=self.emitter,
129
147
  max_turns=max_turns,
148
+ thoroughness=thoroughness,
130
149
  )
131
150
 
151
+ # Emit subagent end event
152
+ if self.emitter:
153
+ from ..events import EventType
154
+ self.emitter.emit(EventType.SUBAGENT_END, {
155
+ "agent_type": subagent_type,
156
+ "success": result.success,
157
+ "iterations": result.iterations,
158
+ "files_explored": len(result.files_explored),
159
+ "execution_time": result.execution_time,
160
+ })
161
+
132
162
  if result.success:
133
163
  return ToolResult.success_result(
134
164
  data=result.to_dict(),
@@ -149,6 +179,7 @@ Multiple sub-agents can be launched in parallel."""
149
179
  subagent_type: str,
150
180
  prompt: str,
151
181
  max_turns: int,
182
+ thoroughness: str = "medium",
152
183
  ) -> ToolResult:
153
184
  """Run sub-agent in background using a thread.
154
185
 
@@ -156,6 +187,7 @@ Multiple sub-agents can be launched in parallel."""
156
187
  subagent_type: Agent type
157
188
  prompt: Task prompt
158
189
  max_turns: Maximum API round-trips
190
+ thoroughness: Search thoroughness level
159
191
 
160
192
  Returns:
161
193
  ToolResult with task info
@@ -175,6 +207,7 @@ Multiple sub-agents can be launched in parallel."""
175
207
  repo_root=self.repo_root,
176
208
  emitter=self.emitter,
177
209
  max_turns=max_turns,
210
+ thoroughness=thoroughness,
178
211
  )
179
212
 
180
213
  # Store future for later retrieval (attach to class for now)
@@ -257,6 +290,12 @@ Multiple sub-agents can be launched in parallel."""
257
290
  "type": "string",
258
291
  "description": "Agent ID to resume from previous execution",
259
292
  },
293
+ "thoroughness": {
294
+ "type": "string",
295
+ "enum": ["quick", "medium", "thorough"],
296
+ "description": "Search thoroughness: quick (basic searches), medium (moderate exploration), thorough (comprehensive analysis)",
297
+ "default": "medium",
298
+ },
260
299
  },
261
300
  required=["prompt"],
262
301
  )
@@ -1,6 +1,6 @@
1
1
  """Task management tools for agent workflows."""
2
2
 
3
- from dataclasses import dataclass, field
3
+ from dataclasses import dataclass
4
4
  from enum import Enum
5
5
  from typing import Optional
6
6
 
@@ -23,9 +23,6 @@ class Task:
23
23
  title: str
24
24
  description: str = ""
25
25
  status: TaskStatus = TaskStatus.PENDING
26
- complexity: str = "M" # T-shirt size: S, M, L, XL
27
- files: list[str] = field(default_factory=list) # Target files for this task
28
- checklist: list[dict] = field(default_factory=list) # Subtasks: [{text, done}]
29
26
 
30
27
  def to_dict(self) -> dict:
31
28
  return {
@@ -33,9 +30,6 @@ class Task:
33
30
  "title": self.title,
34
31
  "description": self.description,
35
32
  "status": self.status.value,
36
- "complexity": self.complexity,
37
- "files": self.files,
38
- "checklist": self.checklist,
39
33
  }
40
34
 
41
35
 
@@ -68,26 +62,17 @@ class TaskState:
68
62
  self,
69
63
  title: str,
70
64
  description: str = "",
71
- complexity: str = "M",
72
- files: list[str] = None,
73
- checklist: list[str] = None,
74
65
  ) -> Task:
75
66
  """Add a new task.
76
67
 
77
68
  Args:
78
69
  title: Task title
79
70
  description: Detailed description
80
- complexity: T-shirt size (S, M, L, XL)
81
- files: List of target file paths
82
- checklist: List of subtask strings (converted to {text, done} dicts)
83
71
  """
84
72
  task = Task(
85
73
  id=str(self._next_id),
86
74
  title=title,
87
75
  description=description,
88
- complexity=complexity,
89
- files=files or [],
90
- checklist=[{"text": item, "done": False} for item in (checklist or [])],
91
76
  )
92
77
  self._next_id += 1
93
78
  self.tasks.append(task)
@@ -139,18 +124,15 @@ class TaskManagementTool(BaseTool):
139
124
 
140
125
 
141
126
  class WriteTodoTool(TaskManagementTool):
142
- """Create a new sub-task for complex work breakdown."""
127
+ """Create a new task for tracking work."""
143
128
 
144
129
  name = "write_todo"
145
- description = "Create a new task with target files and checklist items. Use reset=true to clear all existing tasks first."
130
+ description = "Create a new task. Use reset=true to clear all existing tasks first."
146
131
 
147
132
  def execute(
148
133
  self,
149
134
  title: str,
150
135
  description: str = "",
151
- complexity: str = "M",
152
- files: list[str] = None,
153
- checklist: list[str] = None,
154
136
  reset: bool = False,
155
137
  **kwargs,
156
138
  ) -> ToolResult:
@@ -159,9 +141,6 @@ class WriteTodoTool(TaskManagementTool):
159
141
  Args:
160
142
  title: Short task title
161
143
  description: Detailed description (optional)
162
- complexity: T-shirt size (S, M, L, XL)
163
- files: Target file paths this task will modify
164
- checklist: List of subtask items to track
165
144
  reset: If true, clear all existing tasks before adding this one
166
145
 
167
146
  Returns:
@@ -177,9 +156,6 @@ class WriteTodoTool(TaskManagementTool):
177
156
  task = self.state.add_task(
178
157
  title=title.strip(),
179
158
  description=description.strip() if description else "",
180
- complexity=complexity,
181
- files=files or [],
182
- checklist=checklist or [],
183
159
  )
184
160
 
185
161
  return ToolResult.success_result({
@@ -201,21 +177,6 @@ class WriteTodoTool(TaskManagementTool):
201
177
  "type": "string",
202
178
  "description": "Detailed description of what needs to be done",
203
179
  },
204
- "complexity": {
205
- "type": "string",
206
- "enum": ["S", "M", "L", "XL"],
207
- "description": "T-shirt size complexity: S (trivial), M (moderate), L (significant), XL (complex)",
208
- },
209
- "files": {
210
- "type": "array",
211
- "items": {"type": "string"},
212
- "description": "Target file paths this task will modify",
213
- },
214
- "checklist": {
215
- "type": "array",
216
- "items": {"type": "string"},
217
- "description": "List of subtask items to track (e.g., ['Add imports', 'Update function', 'Add tests'])",
218
- },
219
180
  "reset": {
220
181
  "type": "boolean",
221
182
  "description": "Set to true to clear all existing tasks before adding this one",
@@ -227,39 +188,30 @@ class WriteTodoTool(TaskManagementTool):
227
188
 
228
189
 
229
190
  class UpdateTodoListTool(TaskManagementTool):
230
- """Update task status or checklist items."""
191
+ """Update task status."""
231
192
 
232
193
  name = "update_todo_list"
233
- description = "Update task status, mark checklist items done, or add files. Auto-creates tasks if they don't exist."
194
+ description = "Update task status. Auto-creates tasks if they don't exist."
234
195
 
235
196
  def execute(
236
197
  self,
237
198
  task_id: str,
238
199
  status: str = None,
239
- checklist_done: list[int] = None,
240
- checklist: list[int] = None, # Alias for checklist_done
241
- files: list[str] = None,
242
200
  title: str = "",
243
201
  description: str = "",
244
202
  **kwargs, # Ignore unexpected params from LLM
245
203
  ) -> ToolResult:
246
- """Update task status or checklist items.
204
+ """Update task status.
247
205
 
248
206
  Args:
249
207
  task_id: Task ID to update (e.g., "1", "2")
250
208
  status: New status (pending, in_progress, completed)
251
- checklist_done: Indices of checklist items to mark done (0-based)
252
- checklist: Alias for checklist_done
253
- files: Additional files to add to the task
254
209
  title: Optional title for auto-created tasks
255
210
  description: Optional description for auto-created tasks
256
211
 
257
212
  Returns:
258
213
  ToolResult with updated task list
259
214
  """
260
- # Handle checklist alias
261
- if checklist and not checklist_done:
262
- checklist_done = checklist
263
215
  task = self.state.get_task(task_id)
264
216
 
265
217
  # Auto-create task if not found
@@ -276,7 +228,6 @@ class UpdateTodoListTool(TaskManagementTool):
276
228
  title=title or f"Task {task_id}",
277
229
  status=new_status,
278
230
  description=description,
279
- files=files or [],
280
231
  )
281
232
  self.state.tasks.append(task)
282
233
  return ToolResult.success_result({
@@ -295,18 +246,6 @@ class UpdateTodoListTool(TaskManagementTool):
295
246
  f"Invalid status: {status}. Use: pending, in_progress, completed"
296
247
  )
297
248
 
298
- # Mark checklist items as done
299
- if checklist_done:
300
- for idx in checklist_done:
301
- if 0 <= idx < len(task.checklist):
302
- task.checklist[idx]["done"] = True
303
-
304
- # Add files
305
- if files:
306
- for f in files:
307
- if f not in task.files:
308
- task.files.append(f)
309
-
310
249
  return ToolResult.success_result({
311
250
  "task_id": task_id,
312
251
  "task": task.to_dict(),
@@ -326,16 +265,6 @@ class UpdateTodoListTool(TaskManagementTool):
326
265
  "enum": ["pending", "in_progress", "completed"],
327
266
  "description": "New status for the task",
328
267
  },
329
- "checklist_done": {
330
- "type": "array",
331
- "items": {"type": "integer"},
332
- "description": "Indices of checklist items to mark done (0-based)",
333
- },
334
- "files": {
335
- "type": "array",
336
- "items": {"type": "string"},
337
- "description": "Additional file paths to add to the task",
338
- },
339
268
  "title": {
340
269
  "type": "string",
341
270
  "description": "Task title (used if auto-creating)",
@@ -353,7 +282,13 @@ class AskFollowupQuestionTool(TaskManagementTool):
353
282
  """Request clarification from the user."""
354
283
 
355
284
  name = "ask_followup_question"
356
- description = "Ask the user a clarifying question. Use this when you need more information to proceed."
285
+ description = """Ask the user a clarifying question when you need more information to proceed.
286
+
287
+ CRITICAL CONSTRAINTS:
288
+ - Only ask ONE question at a time - never call this tool multiple times in parallel
289
+ - Only ask AFTER doing research first (read files, search code, explore the codebase)
290
+ - Questions should be informed by what you've learned, not generic upfront questionnaires
291
+ - If you need multiple pieces of information, ask the most important one first"""
357
292
 
358
293
  def execute(
359
294
  self,
@@ -452,3 +387,5 @@ class AttemptCompletionTool(TaskManagementTool):
452
387
  },
453
388
  required=["summary"],
454
389
  )
390
+
391
+