code-puppy 0.0.176__py3-none-any.whl → 0.0.178__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 (34) hide show
  1. code_puppy/agents/__init__.py +2 -2
  2. code_puppy/agents/agent_code_puppy.py +2 -1
  3. code_puppy/agents/agent_creator_agent.py +3 -2
  4. code_puppy/agents/agent_golang_reviewer.py +61 -0
  5. code_puppy/agents/agent_manager.py +5 -5
  6. code_puppy/agents/base_agent.py +60 -40
  7. code_puppy/command_line/command_handler.py +19 -10
  8. code_puppy/command_line/mcp/start_all_command.py +1 -1
  9. code_puppy/command_line/mcp/start_command.py +0 -1
  10. code_puppy/command_line/mcp/stop_all_command.py +1 -1
  11. code_puppy/command_line/mcp/stop_command.py +1 -0
  12. code_puppy/config.py +5 -3
  13. code_puppy/main.py +5 -2
  14. code_puppy/mcp_/examples/retry_example.py +4 -1
  15. code_puppy/messaging/spinner/console_spinner.py +1 -1
  16. code_puppy/model_factory.py +1 -1
  17. code_puppy/round_robin_model.py +2 -4
  18. code_puppy/tools/agent_tools.py +10 -8
  19. code_puppy/tools/browser/browser_screenshot.py +4 -3
  20. code_puppy/tools/browser/browser_scripts.py +0 -6
  21. code_puppy/tools/browser/browser_workflows.py +28 -20
  22. code_puppy/tools/browser/camoufox_manager.py +10 -9
  23. code_puppy/tools/browser_scripts.py +0 -6
  24. code_puppy/tools/browser_workflows.py +28 -20
  25. code_puppy/tools/command_runner.py +1 -1
  26. code_puppy/tui/app.py +3 -13
  27. code_puppy/tui/components/chat_view.py +1 -0
  28. code_puppy/tui/screens/settings.py +3 -3
  29. {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/METADATA +10 -10
  30. {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/RECORD +34 -33
  31. {code_puppy-0.0.176.data → code_puppy-0.0.178.data}/data/code_puppy/models.json +0 -0
  32. {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/WHEEL +0 -0
  33. {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/entry_points.txt +0 -0
  34. {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/licenses/LICENSE +0 -0
@@ -5,14 +5,12 @@ from typing import Any, AsyncIterator, List
5
5
  from pydantic_ai.models import (
6
6
  Model,
7
7
  ModelMessage,
8
- ModelSettings,
9
8
  ModelRequestParameters,
10
9
  ModelResponse,
10
+ ModelSettings,
11
11
  StreamedResponse,
12
12
  )
13
- from pydantic_ai.models.fallback import (
14
- merge_model_settings,
15
- )
13
+ from pydantic_ai.models.fallback import merge_model_settings
16
14
  from pydantic_ai.result import RunContext
17
15
 
18
16
  try:
@@ -1,21 +1,21 @@
1
1
  # agent_tools.py
2
2
 
3
3
  from typing import List
4
+
4
5
  from pydantic import BaseModel
5
- from pydantic_ai import RunContext
6
6
 
7
+ # Import Agent from pydantic_ai to create temporary agents for invocation
8
+ from pydantic_ai import Agent, RunContext
9
+
10
+ from code_puppy.config import get_global_model_name
7
11
  from code_puppy.messaging import (
8
- emit_info,
9
12
  emit_divider,
10
- emit_system_message,
11
13
  emit_error,
14
+ emit_info,
15
+ emit_system_message,
12
16
  )
13
- from code_puppy.tools.common import generate_group_id
14
-
15
- # Import Agent from pydantic_ai to create temporary agents for invocation
16
- from pydantic_ai import Agent
17
17
  from code_puppy.model_factory import ModelFactory
18
- from code_puppy.config import get_global_model_name
18
+ from code_puppy.tools.common import generate_group_id
19
19
 
20
20
 
21
21
  class AgentInfo(BaseModel):
@@ -65,6 +65,7 @@ def register_list_agents(agent):
65
65
 
66
66
  try:
67
67
  from code_puppy.agents import get_available_agents
68
+
68
69
  # Get available agents from the agent manager
69
70
  agents_dict = get_available_agents()
70
71
 
@@ -114,6 +115,7 @@ def register_invoke_agent(agent):
114
115
  AgentInvokeOutput: The agent's response to the prompt
115
116
  """
116
117
  from code_puppy.agents.agent_manager import load_agent
118
+
117
119
  # Generate a group ID for this tool execution
118
120
  group_id = generate_group_id("invoke_agent", agent_name)
119
121
 
@@ -13,10 +13,11 @@ from code_puppy.messaging import emit_error, emit_info
13
13
  from code_puppy.tools.common import generate_group_id
14
14
 
15
15
  from .camoufox_manager import get_camoufox_manager
16
- from .vqa_agent import VisualAnalysisResult, run_vqa_analysis
16
+ from .vqa_agent import run_vqa_analysis
17
17
 
18
-
19
- _TEMP_SCREENSHOT_ROOT = Path(mkdtemp(prefix="code_puppy_screenshots_", dir=gettempdir()))
18
+ _TEMP_SCREENSHOT_ROOT = Path(
19
+ mkdtemp(prefix="code_puppy_screenshots_", dir=gettempdir())
20
+ )
20
21
 
21
22
 
22
23
  def _build_screenshot_path(timestamp: str) -> Path:
@@ -236,9 +236,6 @@ async def wait_for_element(
236
236
  return {"success": False, "error": str(e), "selector": selector, "state": state}
237
237
 
238
238
 
239
-
240
-
241
-
242
239
  async def highlight_element(
243
240
  selector: str,
244
241
  color: str = "red",
@@ -437,9 +434,6 @@ def register_wait_for_element(agent):
437
434
  return await wait_for_element(selector, state, timeout)
438
435
 
439
436
 
440
-
441
-
442
-
443
437
  def register_browser_highlight_element(agent):
444
438
  """Register the element highlighting tool."""
445
439
 
@@ -29,18 +29,18 @@ async def save_workflow(name: str, content: str) -> Dict[str, Any]:
29
29
  workflows_dir = get_workflows_directory()
30
30
 
31
31
  # Clean up the filename - remove spaces, special chars, etc.
32
- safe_name = "".join(c for c in name if c.isalnum() or c in ('-', '_')).lower()
32
+ safe_name = "".join(c for c in name if c.isalnum() or c in ("-", "_")).lower()
33
33
  if not safe_name:
34
34
  safe_name = "workflow"
35
35
 
36
36
  # Ensure .md extension
37
- if not safe_name.endswith('.md'):
38
- safe_name += '.md'
37
+ if not safe_name.endswith(".md"):
38
+ safe_name += ".md"
39
39
 
40
40
  workflow_path = workflows_dir / safe_name
41
41
 
42
42
  # Write the workflow content
43
- with open(workflow_path, 'w', encoding='utf-8') as f:
43
+ with open(workflow_path, "w", encoding="utf-8") as f:
44
44
  f.write(content)
45
45
 
46
46
  emit_info(
@@ -52,7 +52,7 @@ async def save_workflow(name: str, content: str) -> Dict[str, Any]:
52
52
  "success": True,
53
53
  "path": str(workflow_path),
54
54
  "name": safe_name,
55
- "size": len(content)
55
+ "size": len(content),
56
56
  }
57
57
 
58
58
  except Exception as e:
@@ -75,23 +75,27 @@ async def list_workflows() -> Dict[str, Any]:
75
75
  workflows_dir = get_workflows_directory()
76
76
 
77
77
  # Find all .md files in the workflows directory
78
- workflow_files = list(workflows_dir.glob('*.md'))
78
+ workflow_files = list(workflows_dir.glob("*.md"))
79
79
 
80
80
  workflows = []
81
81
  for workflow_file in workflow_files:
82
82
  try:
83
83
  stat = workflow_file.stat()
84
- workflows.append({
85
- "name": workflow_file.name,
86
- "path": str(workflow_file),
87
- "size": stat.st_size,
88
- "modified": stat.st_mtime
89
- })
84
+ workflows.append(
85
+ {
86
+ "name": workflow_file.name,
87
+ "path": str(workflow_file),
88
+ "size": stat.st_size,
89
+ "modified": stat.st_mtime,
90
+ }
91
+ )
90
92
  except Exception as e:
91
- emit_info(f"[yellow]Warning: Could not read {workflow_file}: {e}[/yellow]")
93
+ emit_info(
94
+ f"[yellow]Warning: Could not read {workflow_file}: {e}[/yellow]"
95
+ )
92
96
 
93
97
  # Sort by modification time (newest first)
94
- workflows.sort(key=lambda x: x['modified'], reverse=True)
98
+ workflows.sort(key=lambda x: x["modified"], reverse=True)
95
99
 
96
100
  emit_info(
97
101
  f"[green]✅ Found {len(workflows)} workflow(s)[/green]",
@@ -102,7 +106,7 @@ async def list_workflows() -> Dict[str, Any]:
102
106
  "success": True,
103
107
  "workflows": workflows,
104
108
  "count": len(workflows),
105
- "directory": str(workflows_dir)
109
+ "directory": str(workflows_dir),
106
110
  }
107
111
 
108
112
  except Exception as e:
@@ -125,8 +129,8 @@ async def read_workflow(name: str) -> Dict[str, Any]:
125
129
  workflows_dir = get_workflows_directory()
126
130
 
127
131
  # Handle both with and without .md extension
128
- if not name.endswith('.md'):
129
- name += '.md'
132
+ if not name.endswith(".md"):
133
+ name += ".md"
130
134
 
131
135
  workflow_path = workflows_dir / name
132
136
 
@@ -135,10 +139,14 @@ async def read_workflow(name: str) -> Dict[str, Any]:
135
139
  f"[red]❌ Workflow not found: {name}[/red]",
136
140
  message_group=group_id,
137
141
  )
138
- return {"success": False, "error": f"Workflow '{name}' not found", "name": name}
142
+ return {
143
+ "success": False,
144
+ "error": f"Workflow '{name}' not found",
145
+ "name": name,
146
+ }
139
147
 
140
148
  # Read the workflow content
141
- with open(workflow_path, 'r', encoding='utf-8') as f:
149
+ with open(workflow_path, "r", encoding="utf-8") as f:
142
150
  content = f.read()
143
151
 
144
152
  emit_info(
@@ -151,7 +159,7 @@ async def read_workflow(name: str) -> Dict[str, Any]:
151
159
  "name": name,
152
160
  "content": content,
153
161
  "path": str(workflow_path),
154
- "size": len(content)
162
+ "size": len(content),
155
163
  }
156
164
 
157
165
  except Exception as e:
@@ -3,13 +3,13 @@
3
3
  from typing import Optional
4
4
 
5
5
  import camoufox
6
+ from camoufox.addons import DefaultAddons
7
+ from camoufox.exceptions import CamoufoxNotInstalled, UnsupportedVersion
8
+ from camoufox.locale import ALLOW_GEOIP, download_mmdb
9
+ from camoufox.pkgman import CamoufoxFetcher, camoufox_path
6
10
  from playwright.async_api import Browser, BrowserContext, Page
7
11
 
8
12
  from code_puppy.messaging import emit_info
9
- from camoufox.pkgman import CamoufoxFetcher, camoufox_path
10
- from camoufox.locale import ALLOW_GEOIP, download_mmdb
11
- from camoufox.addons import DefaultAddons
12
- from camoufox.exceptions import CamoufoxNotInstalled, UnsupportedVersion
13
13
 
14
14
 
15
15
  class CamoufoxManager:
@@ -52,7 +52,7 @@ class CamoufoxManager:
52
52
 
53
53
  try:
54
54
  emit_info("[yellow]Initializing Camoufox (privacy Firefox)...[/yellow]")
55
-
55
+
56
56
  # Ensure Camoufox binary and dependencies are fetched before launching
57
57
  await self._prefetch_camoufox()
58
58
 
@@ -62,7 +62,7 @@ class CamoufoxManager:
62
62
  )
63
63
  self._initialized = True
64
64
 
65
- except Exception as e:
65
+ except Exception:
66
66
  await self._cleanup()
67
67
  raise
68
68
 
@@ -83,7 +83,6 @@ class CamoufoxManager:
83
83
  page = await self._context.new_page()
84
84
  await page.goto(self.homepage)
85
85
 
86
-
87
86
  async def get_current_page(self) -> Optional[Page]:
88
87
  """Get the currently active page."""
89
88
  if not self._initialized or not self._context:
@@ -106,7 +105,9 @@ class CamoufoxManager:
106
105
 
107
106
  async def _prefetch_camoufox(self) -> None:
108
107
  """Prefetch Camoufox binary and dependencies."""
109
- emit_info("[cyan]🔍 Ensuring Camoufox binary and dependencies are up-to-date...[/cyan]")
108
+ emit_info(
109
+ "[cyan]🔍 Ensuring Camoufox binary and dependencies are up-to-date...[/cyan]"
110
+ )
110
111
 
111
112
  needs_install = False
112
113
  try:
@@ -168,7 +169,7 @@ class CamoufoxManager:
168
169
  loop.create_task(self._cleanup())
169
170
  else:
170
171
  loop.run_until_complete(self._cleanup())
171
- except:
172
+ except Exception:
172
173
  pass # Best effort cleanup
173
174
 
174
175
 
@@ -236,9 +236,6 @@ async def wait_for_element(
236
236
  return {"success": False, "error": str(e), "selector": selector, "state": state}
237
237
 
238
238
 
239
-
240
-
241
-
242
239
  async def highlight_element(
243
240
  selector: str,
244
241
  color: str = "red",
@@ -437,9 +434,6 @@ def register_wait_for_element(agent):
437
434
  return await wait_for_element(selector, state, timeout)
438
435
 
439
436
 
440
-
441
-
442
-
443
437
  def register_browser_highlight_element(agent):
444
438
  """Register the element highlighting tool."""
445
439
 
@@ -29,18 +29,18 @@ async def save_workflow(name: str, content: str) -> Dict[str, Any]:
29
29
  workflows_dir = get_workflows_directory()
30
30
 
31
31
  # Clean up the filename - remove spaces, special chars, etc.
32
- safe_name = "".join(c for c in name if c.isalnum() or c in ('-', '_')).lower()
32
+ safe_name = "".join(c for c in name if c.isalnum() or c in ("-", "_")).lower()
33
33
  if not safe_name:
34
34
  safe_name = "workflow"
35
35
 
36
36
  # Ensure .md extension
37
- if not safe_name.endswith('.md'):
38
- safe_name += '.md'
37
+ if not safe_name.endswith(".md"):
38
+ safe_name += ".md"
39
39
 
40
40
  workflow_path = workflows_dir / safe_name
41
41
 
42
42
  # Write the workflow content
43
- with open(workflow_path, 'w', encoding='utf-8') as f:
43
+ with open(workflow_path, "w", encoding="utf-8") as f:
44
44
  f.write(content)
45
45
 
46
46
  emit_info(
@@ -52,7 +52,7 @@ async def save_workflow(name: str, content: str) -> Dict[str, Any]:
52
52
  "success": True,
53
53
  "path": str(workflow_path),
54
54
  "name": safe_name,
55
- "size": len(content)
55
+ "size": len(content),
56
56
  }
57
57
 
58
58
  except Exception as e:
@@ -75,23 +75,27 @@ async def list_workflows() -> Dict[str, Any]:
75
75
  workflows_dir = get_workflows_directory()
76
76
 
77
77
  # Find all .md files in the workflows directory
78
- workflow_files = list(workflows_dir.glob('*.md'))
78
+ workflow_files = list(workflows_dir.glob("*.md"))
79
79
 
80
80
  workflows = []
81
81
  for workflow_file in workflow_files:
82
82
  try:
83
83
  stat = workflow_file.stat()
84
- workflows.append({
85
- "name": workflow_file.name,
86
- "path": str(workflow_file),
87
- "size": stat.st_size,
88
- "modified": stat.st_mtime
89
- })
84
+ workflows.append(
85
+ {
86
+ "name": workflow_file.name,
87
+ "path": str(workflow_file),
88
+ "size": stat.st_size,
89
+ "modified": stat.st_mtime,
90
+ }
91
+ )
90
92
  except Exception as e:
91
- emit_info(f"[yellow]Warning: Could not read {workflow_file}: {e}[/yellow]")
93
+ emit_info(
94
+ f"[yellow]Warning: Could not read {workflow_file}: {e}[/yellow]"
95
+ )
92
96
 
93
97
  # Sort by modification time (newest first)
94
- workflows.sort(key=lambda x: x['modified'], reverse=True)
98
+ workflows.sort(key=lambda x: x["modified"], reverse=True)
95
99
 
96
100
  emit_info(
97
101
  f"[green]✅ Found {len(workflows)} workflow(s)[/green]",
@@ -102,7 +106,7 @@ async def list_workflows() -> Dict[str, Any]:
102
106
  "success": True,
103
107
  "workflows": workflows,
104
108
  "count": len(workflows),
105
- "directory": str(workflows_dir)
109
+ "directory": str(workflows_dir),
106
110
  }
107
111
 
108
112
  except Exception as e:
@@ -125,8 +129,8 @@ async def read_workflow(name: str) -> Dict[str, Any]:
125
129
  workflows_dir = get_workflows_directory()
126
130
 
127
131
  # Handle both with and without .md extension
128
- if not name.endswith('.md'):
129
- name += '.md'
132
+ if not name.endswith(".md"):
133
+ name += ".md"
130
134
 
131
135
  workflow_path = workflows_dir / name
132
136
 
@@ -135,10 +139,14 @@ async def read_workflow(name: str) -> Dict[str, Any]:
135
139
  f"[red]❌ Workflow not found: {name}[/red]",
136
140
  message_group=group_id,
137
141
  )
138
- return {"success": False, "error": f"Workflow '{name}' not found", "name": name}
142
+ return {
143
+ "success": False,
144
+ "error": f"Workflow '{name}' not found",
145
+ "name": name,
146
+ }
139
147
 
140
148
  # Read the workflow content
141
- with open(workflow_path, 'r', encoding='utf-8') as f:
149
+ with open(workflow_path, "r", encoding="utf-8") as f:
142
150
  content = f.read()
143
151
 
144
152
  emit_info(
@@ -151,7 +159,7 @@ async def read_workflow(name: str) -> Dict[str, Any]:
151
159
  "name": name,
152
160
  "content": content,
153
161
  "path": str(workflow_path),
154
- "size": len(content)
162
+ "size": len(content),
155
163
  }
156
164
 
157
165
  except Exception as e:
@@ -19,8 +19,8 @@ from code_puppy.messaging import (
19
19
  emit_system_message,
20
20
  emit_warning,
21
21
  )
22
- from code_puppy.tui_state import is_tui_mode
23
22
  from code_puppy.tools.common import generate_group_id
23
+ from code_puppy.tui_state import is_tui_mode
24
24
 
25
25
  # Maximum line length for shell command output to prevent massive token usage
26
26
  # This helps avoid exceeding model context limits when commands produce very long lines
code_puppy/tui/app.py CHANGED
@@ -12,6 +12,8 @@ from textual.events import Resize
12
12
  from textual.reactive import reactive
13
13
  from textual.widgets import Footer, ListView
14
14
 
15
+ # message_history_accumulator and prune_interrupted_tool_calls have been moved to BaseAgent class
16
+ from code_puppy.agents.agent_manager import get_current_agent
15
17
  from code_puppy.command_line.command_handler import handle_command
16
18
  from code_puppy.config import (
17
19
  get_global_model_name,
@@ -19,12 +21,9 @@ from code_puppy.config import (
19
21
  initialize_command_history_file,
20
22
  save_command_to_history,
21
23
  )
22
- # message_history_accumulator and prune_interrupted_tool_calls have been moved to BaseAgent class
23
- from code_puppy.agents.agent_manager import get_current_agent
24
24
 
25
25
  # Import our message queue system
26
26
  from code_puppy.messaging import TUIRenderer, get_global_queue
27
-
28
27
  from code_puppy.tui.components import (
29
28
  ChatView,
30
29
  CustomTextArea,
@@ -33,7 +32,6 @@ from code_puppy.tui.components import (
33
32
  StatusBar,
34
33
  )
35
34
 
36
-
37
35
  # Import shared message classes
38
36
  from .messages import CommandSelected, HistoryEntrySelected
39
37
  from .models import ChatMessage, MessageType
@@ -175,12 +173,6 @@ class CodePuppyTUI(App):
175
173
  "Welcome to Code Puppy 🐶!\n💨 YOLO mode is enabled in TUI: commands will execute without confirmation."
176
174
  )
177
175
 
178
- # Get current agent and display info
179
- agent = get_current_agent()
180
- self.add_system_message(
181
- f"🐕 Loaded agent '{self.puppy_name}' with model '{self.current_model}'"
182
- )
183
-
184
176
  # Start the message renderer EARLY to catch startup messages
185
177
  # Using call_after_refresh to start it as soon as possible after mount
186
178
  self.call_after_refresh(self.start_message_renderer_sync)
@@ -509,9 +501,7 @@ class CodePuppyTUI(App):
509
501
  pass
510
502
  except Exception as agent_error:
511
503
  # Handle any other errors in agent processing
512
- self.add_error_message(
513
- f"Agent processing failed: {str(agent_error)}"
514
- )
504
+ self.add_error_message(f"Agent processing failed: {str(agent_error)}")
515
505
 
516
506
  except Exception as e:
517
507
  self.add_error_message(f"Error processing message: {str(e)}")
@@ -267,6 +267,7 @@ class ChatView(VerticalScroll):
267
267
  ):
268
268
  # If either content is a Rich object, convert both to text and concatenate
269
269
  from io import StringIO
270
+
270
271
  from rich.console import Console
271
272
 
272
273
  # Convert existing content to string
@@ -125,12 +125,12 @@ class SettingsScreen(ModalScreen):
125
125
  def on_mount(self) -> None:
126
126
  """Load current settings when the screen mounts."""
127
127
  from code_puppy.config import (
128
+ get_compaction_strategy,
129
+ get_compaction_threshold,
128
130
  get_global_model_name,
129
131
  get_owner_name,
130
132
  get_protected_token_count,
131
133
  get_puppy_name,
132
- get_compaction_strategy,
133
- get_compaction_threshold,
134
134
  )
135
135
 
136
136
  # Load current values
@@ -188,9 +188,9 @@ class SettingsScreen(ModalScreen):
188
188
  def save_settings(self) -> None:
189
189
  """Save the modified settings."""
190
190
  from code_puppy.config import (
191
+ get_model_context_length,
191
192
  set_config_value,
192
193
  set_model_name,
193
- get_model_context_length,
194
194
  )
195
195
 
196
196
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.176
3
+ Version: 0.0.178
4
4
  Summary: Code generation agent
5
5
  Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
6
6
  Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
@@ -54,15 +54,15 @@ Description-Content-Type: text/markdown
54
54
 
55
55
  ## Overview
56
56
 
57
- *This project was coded angrily in reaction to Windsurf and Cursor removing access to models and raising prices.*
57
+ *This project was coded angrily in reaction to Windsurf and Cursor removing access to models and raising prices.*
58
58
 
59
59
  *You could also run 50 code puppies at once if you were insane enough.*
60
60
 
61
- *Would you rather plow a field with one ox or 1024 puppies?*
61
+ *Would you rather plow a field with one ox or 1024 puppies?*
62
62
  - If you pick the ox, better slam that back button in your browser.
63
-
64
63
 
65
- Code Puppy is an AI-powered code generation agent, designed to understand programming tasks, generate high-quality code, and explain its reasoning similar to tools like Windsurf and Cursor.
64
+
65
+ Code Puppy is an AI-powered code generation agent, designed to understand programming tasks, generate high-quality code, and explain its reasoning similar to tools like Windsurf and Cursor.
66
66
 
67
67
  ## Quick start
68
68
 
@@ -497,22 +497,22 @@ class MyCustomAgent(BaseAgent):
497
497
  @property
498
498
  def name(self) -> str:
499
499
  return "my-agent"
500
-
500
+
501
501
  @property
502
502
  def display_name(self) -> str:
503
503
  return "My Custom Agent ✨"
504
-
504
+
505
505
  @property
506
506
  def description(self) -> str:
507
507
  return "A custom agent for specialized tasks"
508
-
508
+
509
509
  def get_system_prompt(self) -> str:
510
510
  return "Your custom system prompt here..."
511
-
511
+
512
512
  def get_available_tools(self) -> list[str]:
513
513
  return [
514
514
  "list_files",
515
- "read_file",
515
+ "read_file",
516
516
  "grep",
517
517
  "edit_file",
518
518
  "delete_file",