code-puppy 0.0.140__py3-none-any.whl → 0.0.142__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.
- code_puppy/agent.py +1 -1
- code_puppy/agents/agent_code_puppy.py +6 -0
- code_puppy/agents/agent_creator_agent.py +44 -2
- code_puppy/agents/agent_orchestrator.json +26 -0
- code_puppy/agents/runtime_manager.py +24 -17
- code_puppy/main.py +4 -77
- code_puppy/mcp/retry_manager.py +3 -2
- code_puppy/tools/__init__.py +7 -0
- code_puppy/tools/agent_tools.py +272 -0
- {code_puppy-0.0.140.dist-info → code_puppy-0.0.142.dist-info}/METADATA +1 -1
- {code_puppy-0.0.140.dist-info → code_puppy-0.0.142.dist-info}/RECORD +15 -13
- {code_puppy-0.0.140.data → code_puppy-0.0.142.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.140.dist-info → code_puppy-0.0.142.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.140.dist-info → code_puppy-0.0.142.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.140.dist-info → code_puppy-0.0.142.dist-info}/licenses/LICENSE +0 -0
code_puppy/agent.py
CHANGED
|
@@ -150,7 +150,7 @@ def reload_code_generation_agent(message_group: str | None):
|
|
|
150
150
|
|
|
151
151
|
# Configure model settings with max_tokens if set
|
|
152
152
|
model_settings_dict = {"seed": 42}
|
|
153
|
-
output_tokens = min(int(0.05 * get_model_context_length()) - 1024, 16384)
|
|
153
|
+
output_tokens = max(2048, min(int(0.05 * get_model_context_length()) - 1024, 16384))
|
|
154
154
|
console.print(f"Max output tokens per message: {output_tokens}")
|
|
155
155
|
model_settings_dict["max_tokens"] = output_tokens
|
|
156
156
|
|
|
@@ -23,6 +23,8 @@ class CodePuppyAgent(BaseAgent):
|
|
|
23
23
|
def get_available_tools(self) -> list[str]:
|
|
24
24
|
"""Get the list of tools available to Code-Puppy."""
|
|
25
25
|
return [
|
|
26
|
+
"list_agents",
|
|
27
|
+
"invoke_agent",
|
|
26
28
|
"list_files",
|
|
27
29
|
"read_file",
|
|
28
30
|
"grep",
|
|
@@ -128,6 +130,10 @@ DONT USE THE TERMINAL TOOL TO RUN THE CODE WE WROTE UNLESS THE USER ASKS YOU TO.
|
|
|
128
130
|
Reasoning & Explanation:
|
|
129
131
|
- share_your_reasoning(reasoning, next_steps=None): Use this to explicitly share your thought process and planned next steps
|
|
130
132
|
|
|
133
|
+
Agent Management:
|
|
134
|
+
- list_agents(): Use this to list all available sub-agents that can be invoked
|
|
135
|
+
- invoke_agent(agent_name: str, prompt: str): Use this to invoke a specific sub-agent with a given prompt
|
|
136
|
+
|
|
131
137
|
Important rules:
|
|
132
138
|
- You MUST use tools to accomplish tasks - DO NOT just output code or descriptions
|
|
133
139
|
- Before every other tool use, you must use "share_your_reasoning" to explain your thought process and planned next steps
|
|
@@ -95,6 +95,8 @@ Here's the complete schema for JSON agent files:
|
|
|
95
95
|
|
|
96
96
|
### 🧠 **Communication & Reasoning** (for all agents):
|
|
97
97
|
- `agent_share_your_reasoning` - Explain thought processes (recommended for most agents)
|
|
98
|
+
- `list_agents` - List all available sub-agents (recommended for agent managers)
|
|
99
|
+
- `invoke_agent` - Invoke other agents with specific prompts (recommended for agent managers)
|
|
98
100
|
|
|
99
101
|
## Detailed Tool Documentation (Instructions for Agent Creation)
|
|
100
102
|
|
|
@@ -178,6 +180,27 @@ DONT USE THE TERMINAL TOOL TO RUN THE CODE WE WROTE UNLESS THE USER ASKS YOU TO.
|
|
|
178
180
|
#### `agent_share_your_reasoning(reasoning, next_steps=None)`
|
|
179
181
|
Use this to explicitly share your thought process and planned next steps
|
|
180
182
|
|
|
183
|
+
#### `list_agents()`
|
|
184
|
+
Use this to list all available sub-agents that can be invoked
|
|
185
|
+
|
|
186
|
+
#### `invoke_agent(agent_name: str, user_prompt: str)`
|
|
187
|
+
Use this to invoke another agent with a specific prompt. This allows agents to delegate tasks to specialized sub-agents.
|
|
188
|
+
|
|
189
|
+
Arguments:
|
|
190
|
+
- agent_name (required): Name of the agent to invoke
|
|
191
|
+
- user_prompt (required): The prompt to send to the invoked agent
|
|
192
|
+
|
|
193
|
+
Example usage:
|
|
194
|
+
```python
|
|
195
|
+
invoke_agent(agent_name="python-tutor", user_prompt="Explain how to use list comprehensions")
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Best-practice guidelines for `invoke_agent`:
|
|
199
|
+
• Only invoke agents that exist (use `list_agents` to verify)
|
|
200
|
+
• Clearly specify what you want the invoked agent to do
|
|
201
|
+
• Be specific in your prompts to get better results
|
|
202
|
+
• Avoid circular dependencies (don't invoke yourself!)
|
|
203
|
+
|
|
181
204
|
### Important Rules for Agent Creation:
|
|
182
205
|
- You MUST use tools to accomplish tasks - DO NOT just output code or descriptions
|
|
183
206
|
- Before every other tool use, you must use "share_your_reasoning" to explain your thought process and planned next steps
|
|
@@ -207,6 +230,8 @@ Available templates for tools:
|
|
|
207
230
|
- `grep`: Standard text search operations
|
|
208
231
|
- `agent_run_shell_command`: Standard shell command execution
|
|
209
232
|
- `agent_share_your_reasoning`: Standard reasoning sharing operations
|
|
233
|
+
- `list_agents`: Standard agent listing operations
|
|
234
|
+
- `invoke_agent`: Standard agent invocation operations
|
|
210
235
|
|
|
211
236
|
Each agent you create should only include templates for tools it actually uses. The `edit_file` tool template
|
|
212
237
|
should always include its detailed usage instructions when selected.
|
|
@@ -275,6 +300,7 @@ This detailed documentation should be copied verbatim into any agent that will b
|
|
|
275
300
|
**For "System admin helper":** → Suggest `agent_run_shell_command`, `list_files`, `read_file`, `agent_share_your_reasoning`
|
|
276
301
|
**For "Code reviewer":** → Suggest `list_files`, `read_file`, `grep`, `agent_share_your_reasoning`
|
|
277
302
|
**For "File organizer":** → Suggest `list_files`, `read_file`, `edit_file`, `delete_file`, `agent_share_your_reasoning`
|
|
303
|
+
**For "Agent orchestrator":** → Suggest `list_agents`, `invoke_agent`, `agent_share_your_reasoning`
|
|
278
304
|
|
|
279
305
|
## Best Practices
|
|
280
306
|
|
|
@@ -322,6 +348,22 @@ This detailed documentation should be copied verbatim into any agent that will b
|
|
|
322
348
|
}}
|
|
323
349
|
```
|
|
324
350
|
|
|
351
|
+
**Agent Manager:**
|
|
352
|
+
```json
|
|
353
|
+
{{
|
|
354
|
+
"name": "agent-manager",
|
|
355
|
+
"display_name": "Agent Manager 🎭",
|
|
356
|
+
"description": "Manages and orchestrates other agents to accomplish complex tasks",
|
|
357
|
+
"system_prompt": [
|
|
358
|
+
"You are an agent manager that orchestrates other specialized agents.",
|
|
359
|
+
"You help users accomplish tasks by delegating to the appropriate sub-agent.",
|
|
360
|
+
"You coordinate between multiple agents to get complex work done."
|
|
361
|
+
],
|
|
362
|
+
"tools": ["list_agents", "invoke_agent", "agent_share_your_reasoning"],
|
|
363
|
+
"user_prompt": "What can I help you accomplish today?"
|
|
364
|
+
}}
|
|
365
|
+
```
|
|
366
|
+
|
|
325
367
|
You're fun, enthusiastic, and love helping people create amazing agents! 🚀
|
|
326
368
|
|
|
327
369
|
Be interactive - ask questions, suggest improvements, and guide users through the process step by step.
|
|
@@ -348,7 +390,7 @@ Your goal is to take users from idea to working agent in one smooth conversation
|
|
|
348
390
|
|
|
349
391
|
def get_available_tools(self) -> List[str]:
|
|
350
392
|
"""Get all tools needed for agent creation."""
|
|
351
|
-
return ["list_files", "read_file", "edit_file", "agent_share_your_reasoning"]
|
|
393
|
+
return ["list_files", "read_file", "edit_file", "agent_share_your_reasoning", "list_agents", "invoke_agent"]
|
|
352
394
|
|
|
353
395
|
def validate_agent_json(self, agent_config: Dict) -> List[str]:
|
|
354
396
|
"""Validate a JSON agent configuration.
|
|
@@ -443,4 +485,4 @@ Your goal is to take users from idea to working agent in one smooth conversation
|
|
|
443
485
|
|
|
444
486
|
def get_user_prompt(self) -> Optional[str]:
|
|
445
487
|
"""Get the initial user prompt."""
|
|
446
|
-
return "Hi! I'm the Agent Creator 🏗️ Let's build an awesome agent together!"
|
|
488
|
+
return "Hi! I'm the Agent Creator 🏗️ Let's build an awesome agent together!"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "agent-orchestrator-id",
|
|
3
|
+
"name": "agent-orchestrator",
|
|
4
|
+
"display_name": "Agent Orchestrator 🎭",
|
|
5
|
+
"description": "Coordinates and manages various specialized agents to accomplish tasks",
|
|
6
|
+
"system_prompt": [
|
|
7
|
+
"You are an agent orchestrator that coordinates various specialized agents.",
|
|
8
|
+
"When given a task, first list the available agents to understand what's at your disposal.",
|
|
9
|
+
"Then, invoke the most appropriate agent to handle the task. If needed, you can invoke multiple agents.",
|
|
10
|
+
"",
|
|
11
|
+
"#### `list_agents()`",
|
|
12
|
+
"Use this to list all available sub-agents that can be invoked",
|
|
13
|
+
"",
|
|
14
|
+
"#### `invoke_agent(agent_name: str, user_prompt: str)`",
|
|
15
|
+
"Use this to invoke another agent with a specific prompt. This allows agents to delegate tasks to specialized sub-agents.",
|
|
16
|
+
"Arguments:",
|
|
17
|
+
"- agent_name (required): Name of the agent to invoke",
|
|
18
|
+
"- user_prompt (required): The prompt to send to the invoked agent",
|
|
19
|
+
"Example usage:",
|
|
20
|
+
"```python",
|
|
21
|
+
"invoke_agent(agent_name=\"python-tutor\", user_prompt=\"Explain how to use list comprehensions\")",
|
|
22
|
+
"```"
|
|
23
|
+
],
|
|
24
|
+
"tools": ["list_agents", "invoke_agent", "agent_share_your_reasoning"],
|
|
25
|
+
"user_prompt": "What would you like me to coordinate for you?"
|
|
26
|
+
}
|
|
@@ -24,6 +24,7 @@ else:
|
|
|
24
24
|
|
|
25
25
|
import mcp
|
|
26
26
|
from pydantic_ai import Agent
|
|
27
|
+
from pydantic_ai.exceptions import UsageLimitExceeded
|
|
27
28
|
from pydantic_ai.usage import UsageLimits
|
|
28
29
|
|
|
29
30
|
from code_puppy.messaging.message_queue import emit_info, emit_warning
|
|
@@ -110,26 +111,31 @@ class RuntimeAgentManager:
|
|
|
110
111
|
try:
|
|
111
112
|
async with agent:
|
|
112
113
|
return await agent.run(prompt, usage_limits=usage_limits, **kwargs)
|
|
114
|
+
except* UsageLimitExceeded as ule:
|
|
115
|
+
emit_info(f"Usage limit exceeded: {str(ule)}", group_id=group_id)
|
|
116
|
+
emit_info("The agent has reached its usage limit. You can ask it to continue by saying 'please continue' or similar.", group_id=group_id)
|
|
113
117
|
except* mcp.shared.exceptions.McpError as mcp_error:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
emit_info(f"MCP server error: {str(mcp_error)}", group_id=group_id)
|
|
119
|
+
emit_info(f"{str(mcp_error)}", group_id=group_id)
|
|
120
|
+
emit_info(
|
|
117
121
|
"Try disabling any malfunctioning MCP servers", group_id=group_id
|
|
118
122
|
)
|
|
123
|
+
except* asyncio.exceptions.CancelledError:
|
|
124
|
+
emit_info("Cancelled")
|
|
119
125
|
except* InterruptedError as ie:
|
|
120
|
-
|
|
126
|
+
emit_info(f"Interrupted: {str(ie)}")
|
|
121
127
|
except* Exception as other_error:
|
|
122
|
-
# Filter out CancelledError from the exception group - let it propagate
|
|
128
|
+
# Filter out CancelledError and UsageLimitExceeded from the exception group - let it propagate
|
|
123
129
|
remaining_exceptions = []
|
|
124
130
|
|
|
125
131
|
def collect_non_cancelled_exceptions(exc):
|
|
126
132
|
if isinstance(exc, ExceptionGroup):
|
|
127
133
|
for sub_exc in exc.exceptions:
|
|
128
134
|
collect_non_cancelled_exceptions(sub_exc)
|
|
129
|
-
elif not isinstance(exc, asyncio.CancelledError):
|
|
135
|
+
elif not isinstance(exc, (asyncio.CancelledError, UsageLimitExceeded)):
|
|
130
136
|
remaining_exceptions.append(exc)
|
|
131
|
-
|
|
132
|
-
|
|
137
|
+
emit_info(f"Unexpected error: {str(exc)}", group_id=group_id)
|
|
138
|
+
emit_info(f"{str(exc.args)}", group_id=group_id)
|
|
133
139
|
|
|
134
140
|
collect_non_cancelled_exceptions(other_error)
|
|
135
141
|
|
|
@@ -156,26 +162,20 @@ class RuntimeAgentManager:
|
|
|
156
162
|
from code_puppy.tools.command_runner import kill_all_running_shell_processes
|
|
157
163
|
|
|
158
164
|
# Ensure the interrupt handler only acts once per task
|
|
159
|
-
handled = False
|
|
160
|
-
|
|
161
165
|
def keyboard_interrupt_handler(sig, frame):
|
|
162
166
|
"""Signal handler for Ctrl+C - replicating exact original logic"""
|
|
163
|
-
nonlocal handled
|
|
164
|
-
if handled:
|
|
165
|
-
return
|
|
166
|
-
handled = True
|
|
167
167
|
|
|
168
168
|
# First, nuke any running shell processes triggered by tools
|
|
169
169
|
try:
|
|
170
170
|
killed = kill_all_running_shell_processes()
|
|
171
171
|
if killed:
|
|
172
|
-
|
|
172
|
+
emit_info(f"Cancelled {killed} running shell process(es).")
|
|
173
173
|
else:
|
|
174
174
|
# Only cancel the agent task if no shell processes were killed
|
|
175
175
|
if not agent_task.done():
|
|
176
176
|
agent_task.cancel()
|
|
177
177
|
except Exception as e:
|
|
178
|
-
|
|
178
|
+
emit_info(f"Shell kill error: {e}")
|
|
179
179
|
# If shell kill failed, still try to cancel the agent task
|
|
180
180
|
if not agent_task.done():
|
|
181
181
|
agent_task.cancel()
|
|
@@ -221,7 +221,14 @@ class RuntimeAgentManager:
|
|
|
221
221
|
The agent's response
|
|
222
222
|
"""
|
|
223
223
|
agent = self.get_agent()
|
|
224
|
-
|
|
224
|
+
try:
|
|
225
|
+
return await agent.run(prompt, usage_limits=usage_limits, **kwargs)
|
|
226
|
+
except UsageLimitExceeded as ule:
|
|
227
|
+
group_id = str(uuid.uuid4())
|
|
228
|
+
emit_info(f"Usage limit exceeded: {str(ule)}", group_id=group_id)
|
|
229
|
+
emit_info("The agent has reached its usage limit. You can ask it to continue by saying 'please continue' or similar.", group_id=group_id)
|
|
230
|
+
# Return None or some default value to indicate the limit was reached
|
|
231
|
+
return None
|
|
225
232
|
|
|
226
233
|
def __getattr__(self, name: str) -> Any:
|
|
227
234
|
"""
|
code_puppy/main.py
CHANGED
|
@@ -132,7 +132,7 @@ async def main():
|
|
|
132
132
|
)
|
|
133
133
|
direct_console.print("[yellow]Press Ctrl+C to stop the server.[/yellow]\n")
|
|
134
134
|
process = subprocess.Popen(textual_serve_cmd)
|
|
135
|
-
time.sleep(
|
|
135
|
+
time.sleep(0.3)
|
|
136
136
|
try:
|
|
137
137
|
direct_console.print(
|
|
138
138
|
"[cyan]🚀 Opening web interface in your default browser...[/cyan]"
|
|
@@ -424,81 +424,11 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
424
424
|
from code_puppy.messaging import emit_warning
|
|
425
425
|
from code_puppy.messaging.spinner import ConsoleSpinner
|
|
426
426
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
False # Prevent multiple signal handler calls (reset per task)
|
|
431
|
-
)
|
|
432
|
-
|
|
433
|
-
async def run_task():
|
|
434
|
-
# Use the simpler run() method instead of run_with_mcp() to avoid signal handler
|
|
435
|
-
agent = agent_manager.get_agent()
|
|
436
|
-
async with agent:
|
|
437
|
-
return await agent.run(
|
|
438
|
-
task,
|
|
439
|
-
message_history=get_message_history(),
|
|
440
|
-
usage_limits=get_custom_usage_limits(),
|
|
441
|
-
)
|
|
442
|
-
|
|
443
|
-
def handle_keyboard_interrupt():
|
|
444
|
-
"""Handle Ctrl+C like TUI does - kill processes but only cancel task if no processes killed"""
|
|
445
|
-
nonlocal signal_handled
|
|
446
|
-
if signal_handled:
|
|
447
|
-
return
|
|
448
|
-
signal_handled = True
|
|
449
|
-
|
|
450
|
-
from code_puppy.tools.command_runner import (
|
|
451
|
-
kill_all_running_shell_processes,
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
killed = kill_all_running_shell_processes()
|
|
455
|
-
if killed:
|
|
456
|
-
emit_warning(f"🔥 Cancelled {killed} running shell process(es)")
|
|
457
|
-
# Don't cancel the agent task - let it continue processing
|
|
458
|
-
# Shell processes killed, but agent continues running
|
|
459
|
-
else:
|
|
460
|
-
# Only cancel the agent task if NO processes were killed
|
|
461
|
-
if current_task and not current_task.done():
|
|
462
|
-
current_task.cancel()
|
|
463
|
-
emit_warning("⚠️ Processing cancelled by user")
|
|
464
|
-
|
|
465
|
-
# Set up proper signal handling to override asyncio's default behavior
|
|
466
|
-
import signal
|
|
467
|
-
|
|
468
|
-
def signal_handler(sig, frame):
|
|
469
|
-
"""Handle Ctrl+C by killing processes and cancelling the current task"""
|
|
470
|
-
handle_keyboard_interrupt()
|
|
471
|
-
|
|
472
|
-
# Replace asyncio's SIGINT handler with our own
|
|
473
|
-
original_handler = signal.signal(signal.SIGINT, signal_handler)
|
|
474
|
-
|
|
475
|
-
# Use ConsoleSpinner for better user experience
|
|
476
|
-
try:
|
|
477
|
-
with ConsoleSpinner(console=display_console):
|
|
478
|
-
current_task = asyncio.create_task(run_task())
|
|
479
|
-
result = await current_task
|
|
480
|
-
except asyncio.CancelledError:
|
|
481
|
-
# Agent was cancelled by our signal handler
|
|
482
|
-
result = None
|
|
483
|
-
except KeyboardInterrupt:
|
|
484
|
-
# Fallback - handle Ctrl+C if it gets through as KeyboardInterrupt
|
|
485
|
-
emit_warning("\n⚠️ Caught KeyboardInterrupt")
|
|
486
|
-
handle_keyboard_interrupt()
|
|
487
|
-
result = None
|
|
488
|
-
finally:
|
|
489
|
-
# Restore original signal handler
|
|
490
|
-
if "original_handler" in locals():
|
|
491
|
-
signal.signal(signal.SIGINT, original_handler)
|
|
492
|
-
set_message_history(
|
|
493
|
-
prune_interrupted_tool_calls(get_message_history())
|
|
494
|
-
)
|
|
495
|
-
|
|
427
|
+
runtime_manager = get_runtime_agent_manager()
|
|
428
|
+
with ConsoleSpinner(console=message_renderer.console):
|
|
429
|
+
result = await runtime_manager.run_with_mcp(task, get_custom_usage_limits())
|
|
496
430
|
# Check if the task was cancelled (but don't show message if we just killed processes)
|
|
497
431
|
if result is None:
|
|
498
|
-
# Only show cancellation message if we actually cancelled the agent task
|
|
499
|
-
# If we just killed shell processes, the agent should continue normally
|
|
500
|
-
pass # Don't always show this message
|
|
501
|
-
# Skip the rest of this loop iteration
|
|
502
432
|
continue
|
|
503
433
|
# Get the structured response
|
|
504
434
|
agent_response = result.output
|
|
@@ -512,9 +442,6 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
512
442
|
new_msgs = result.all_messages()
|
|
513
443
|
message_history_accumulator(new_msgs)
|
|
514
444
|
|
|
515
|
-
# Show context status
|
|
516
|
-
from code_puppy.messaging import emit_system_message
|
|
517
|
-
|
|
518
445
|
emit_system_message(
|
|
519
446
|
f"Context: {len(get_message_history())} messages in history\n"
|
|
520
447
|
)
|
code_puppy/mcp/retry_manager.py
CHANGED
|
@@ -31,8 +31,9 @@ class RetryStats:
|
|
|
31
31
|
def calculate_average(self, new_attempts: int) -> None:
|
|
32
32
|
"""Update the average attempts calculation."""
|
|
33
33
|
if self.total_retries == 0:
|
|
34
|
-
self.average_attempts = new_attempts
|
|
34
|
+
self.average_attempts = float(new_attempts)
|
|
35
35
|
else:
|
|
36
|
+
# Calculate new average: (old_average * old_count + new_value) / new_count
|
|
36
37
|
total_attempts = (self.average_attempts * self.total_retries) + new_attempts
|
|
37
38
|
self.average_attempts = total_attempts / (self.total_retries + 1)
|
|
38
39
|
|
|
@@ -214,7 +215,6 @@ class RetryManager:
|
|
|
214
215
|
"""
|
|
215
216
|
async with self._lock:
|
|
216
217
|
stats = self._stats[server_id]
|
|
217
|
-
stats.total_retries += 1
|
|
218
218
|
stats.last_retry = datetime.now()
|
|
219
219
|
|
|
220
220
|
if success:
|
|
@@ -223,6 +223,7 @@ class RetryManager:
|
|
|
223
223
|
stats.failed_retries += 1
|
|
224
224
|
|
|
225
225
|
stats.calculate_average(attempts)
|
|
226
|
+
stats.total_retries += 1
|
|
226
227
|
|
|
227
228
|
async def get_retry_stats(self, server_id: str) -> RetryStats:
|
|
228
229
|
"""
|
code_puppy/tools/__init__.py
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
from code_puppy.messaging import emit_warning
|
|
2
|
+
from code_puppy.tools.agent_tools import (
|
|
3
|
+
register_list_agents,
|
|
4
|
+
register_invoke_agent,
|
|
5
|
+
)
|
|
2
6
|
from code_puppy.tools.command_runner import (
|
|
3
7
|
register_agent_run_shell_command,
|
|
4
8
|
register_agent_share_your_reasoning,
|
|
@@ -13,6 +17,9 @@ from code_puppy.tools.file_operations import (
|
|
|
13
17
|
|
|
14
18
|
# Map of tool names to their individual registration functions
|
|
15
19
|
TOOL_REGISTRY = {
|
|
20
|
+
# Agent Tools
|
|
21
|
+
"list_agents": register_list_agents,
|
|
22
|
+
"invoke_agent": register_invoke_agent,
|
|
16
23
|
# File Operations
|
|
17
24
|
"list_files": register_list_files,
|
|
18
25
|
"read_file": register_read_file,
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# agent_tools.py
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from pydantic_ai import RunContext
|
|
6
|
+
|
|
7
|
+
from code_puppy.messaging import (
|
|
8
|
+
emit_info,
|
|
9
|
+
emit_divider,
|
|
10
|
+
emit_system_message,
|
|
11
|
+
emit_error,
|
|
12
|
+
)
|
|
13
|
+
from code_puppy.tools.common import generate_group_id
|
|
14
|
+
from code_puppy.agents.agent_manager import get_available_agents, load_agent_config
|
|
15
|
+
|
|
16
|
+
# Import Agent from pydantic_ai to create temporary agents for invocation
|
|
17
|
+
from pydantic_ai import Agent
|
|
18
|
+
from code_puppy.model_factory import ModelFactory
|
|
19
|
+
from code_puppy.config import get_model_name
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AgentInfo(BaseModel):
|
|
23
|
+
"""Information about an available agent."""
|
|
24
|
+
name: str
|
|
25
|
+
display_name: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ListAgentsOutput(BaseModel):
|
|
29
|
+
"""Output for the list_agents tool."""
|
|
30
|
+
agents: List[AgentInfo]
|
|
31
|
+
error: str | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AgentInvokeOutput(BaseModel):
|
|
35
|
+
"""Output for the invoke_agent tool."""
|
|
36
|
+
response: str | None
|
|
37
|
+
agent_name: str
|
|
38
|
+
error: str | None = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _list_agents(context: RunContext) -> ListAgentsOutput:
|
|
42
|
+
"""List all available sub-agents that can be invoked.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
ListAgentsOutput: A list of available agents with their names and display names.
|
|
46
|
+
"""
|
|
47
|
+
group_id = generate_group_id("list_agents")
|
|
48
|
+
|
|
49
|
+
emit_info(
|
|
50
|
+
"\n[bold white on blue] LIST AGENTS [/bold white on blue]",
|
|
51
|
+
message_group=group_id
|
|
52
|
+
)
|
|
53
|
+
emit_divider(message_group=group_id)
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
# Get available agents from the agent manager
|
|
57
|
+
agents_dict = get_available_agents()
|
|
58
|
+
|
|
59
|
+
# Convert to list of AgentInfo objects
|
|
60
|
+
agents = [
|
|
61
|
+
AgentInfo(name=name, display_name=display_name)
|
|
62
|
+
for name, display_name in agents_dict.items()
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
# Display the agents in the console
|
|
66
|
+
for agent in agents:
|
|
67
|
+
emit_system_message(
|
|
68
|
+
f"- [bold]{agent.name}[/bold]: {agent.display_name}",
|
|
69
|
+
message_group=group_id
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
emit_divider(message_group=group_id)
|
|
73
|
+
return ListAgentsOutput(agents=agents)
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
error_msg = f"Error listing agents: {str(e)}"
|
|
77
|
+
emit_error(error_msg, message_group=group_id)
|
|
78
|
+
emit_divider(message_group=group_id)
|
|
79
|
+
return ListAgentsOutput(agents=[], error=error_msg)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _invoke_agent(context: RunContext, agent_name: str, prompt: str) -> AgentInvokeOutput:
|
|
83
|
+
"""Invoke a specific sub-agent with a given prompt.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
agent_name: The name of the agent to invoke
|
|
87
|
+
prompt: The prompt to send to the agent
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
AgentInvokeOutput: The agent's response to the prompt
|
|
91
|
+
"""
|
|
92
|
+
group_id = generate_group_id("invoke_agent", agent_name)
|
|
93
|
+
|
|
94
|
+
emit_info(
|
|
95
|
+
f"\n[bold white on blue] INVOKE AGENT [/bold white on blue] {agent_name}",
|
|
96
|
+
message_group=group_id
|
|
97
|
+
)
|
|
98
|
+
emit_divider(message_group=group_id)
|
|
99
|
+
emit_system_message(f"Prompt: {prompt}", message_group=group_id)
|
|
100
|
+
emit_divider(message_group=group_id)
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
# Load the specified agent config
|
|
104
|
+
agent_config = load_agent_config(agent_name)
|
|
105
|
+
|
|
106
|
+
# Get the current model for creating a temporary agent
|
|
107
|
+
model_name = get_model_name()
|
|
108
|
+
models_config = ModelFactory.load_config()
|
|
109
|
+
model = ModelFactory.get_model(model_name, models_config)
|
|
110
|
+
|
|
111
|
+
# Create a temporary agent instance to avoid interfering with current agent state
|
|
112
|
+
instructions = agent_config.get_system_prompt()
|
|
113
|
+
temp_agent = Agent(
|
|
114
|
+
model=model,
|
|
115
|
+
instructions=instructions,
|
|
116
|
+
output_type=str,
|
|
117
|
+
retries=3,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Register the tools that the agent needs
|
|
121
|
+
from code_puppy.tools import register_tools_for_agent
|
|
122
|
+
agent_tools = agent_config.get_available_tools()
|
|
123
|
+
|
|
124
|
+
# Avoid recursive tool registration - if the agent has the same tools
|
|
125
|
+
# as the current agent, skip registration to prevent conflicts
|
|
126
|
+
current_agent_tools = ["list_agents", "invoke_agent"]
|
|
127
|
+
if set(agent_tools) != set(current_agent_tools):
|
|
128
|
+
register_tools_for_agent(temp_agent, agent_tools)
|
|
129
|
+
|
|
130
|
+
# Run the temporary agent with the provided prompt
|
|
131
|
+
result = temp_agent.run_sync(prompt)
|
|
132
|
+
|
|
133
|
+
# Extract the response from the result
|
|
134
|
+
response = result.output
|
|
135
|
+
|
|
136
|
+
emit_system_message(f"Response: {response}", message_group=group_id)
|
|
137
|
+
emit_divider(message_group=group_id)
|
|
138
|
+
|
|
139
|
+
return AgentInvokeOutput(response=response, agent_name=agent_name)
|
|
140
|
+
|
|
141
|
+
except Exception as e:
|
|
142
|
+
error_msg = f"Error invoking agent '{agent_name}': {str(e)}"
|
|
143
|
+
emit_error(error_msg, message_group=group_id)
|
|
144
|
+
emit_divider(message_group=group_id)
|
|
145
|
+
return AgentInvokeOutput(response=None, agent_name=agent_name, error=error_msg)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def register_list_agents(agent):
|
|
149
|
+
"""Register the list_agents tool with the provided agent.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
agent: The agent to register the tool with
|
|
153
|
+
"""
|
|
154
|
+
@agent.tool
|
|
155
|
+
def list_agents(context: RunContext) -> ListAgentsOutput:
|
|
156
|
+
"""List all available sub-agents that can be invoked.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
ListAgentsOutput: A list of available agents with their names and display names.
|
|
160
|
+
"""
|
|
161
|
+
# Generate a group ID for this tool execution
|
|
162
|
+
group_id = generate_group_id("list_agents")
|
|
163
|
+
|
|
164
|
+
emit_info(
|
|
165
|
+
"\n[bold white on blue] LIST AGENTS [/bold white on blue]",
|
|
166
|
+
message_group=group_id
|
|
167
|
+
)
|
|
168
|
+
emit_divider(message_group=group_id)
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
# Get available agents from the agent manager
|
|
172
|
+
agents_dict = get_available_agents()
|
|
173
|
+
|
|
174
|
+
# Convert to list of AgentInfo objects
|
|
175
|
+
agents = [
|
|
176
|
+
AgentInfo(name=name, display_name=display_name)
|
|
177
|
+
for name, display_name in agents_dict.items()
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
# Display the agents in the console
|
|
181
|
+
for agent_item in agents:
|
|
182
|
+
emit_system_message(
|
|
183
|
+
f"- [bold]{agent_item.name}[/bold]: {agent_item.display_name}",
|
|
184
|
+
message_group=group_id
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
emit_divider(message_group=group_id)
|
|
188
|
+
return ListAgentsOutput(agents=agents)
|
|
189
|
+
|
|
190
|
+
except Exception as e:
|
|
191
|
+
error_msg = f"Error listing agents: {str(e)}"
|
|
192
|
+
emit_error(error_msg, message_group=group_id)
|
|
193
|
+
emit_divider(message_group=group_id)
|
|
194
|
+
return ListAgentsOutput(agents=[], error=error_msg)
|
|
195
|
+
|
|
196
|
+
return list_agents
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def register_invoke_agent(agent):
|
|
200
|
+
"""Register the invoke_agent tool with the provided agent.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
agent: The agent to register the tool with
|
|
204
|
+
"""
|
|
205
|
+
@agent.tool
|
|
206
|
+
def invoke_agent(context: RunContext, agent_name: str, prompt: str) -> AgentInvokeOutput:
|
|
207
|
+
"""Invoke a specific sub-agent with a given prompt.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
agent_name: The name of the agent to invoke
|
|
211
|
+
prompt: The prompt to send to the agent
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
AgentInvokeOutput: The agent's response to the prompt
|
|
215
|
+
"""
|
|
216
|
+
# Generate a group ID for this tool execution
|
|
217
|
+
group_id = generate_group_id("invoke_agent", agent_name)
|
|
218
|
+
|
|
219
|
+
emit_info(
|
|
220
|
+
f"\n[bold white on blue] INVOKE AGENT [/bold white on blue] {agent_name}",
|
|
221
|
+
message_group=group_id
|
|
222
|
+
)
|
|
223
|
+
emit_divider(message_group=group_id)
|
|
224
|
+
emit_system_message(f"Prompt: {prompt}", message_group=group_id)
|
|
225
|
+
emit_divider(message_group=group_id)
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
# Load the specified agent config
|
|
229
|
+
agent_config = load_agent_config(agent_name)
|
|
230
|
+
|
|
231
|
+
# Get the current model for creating a temporary agent
|
|
232
|
+
model_name = get_model_name()
|
|
233
|
+
models_config = ModelFactory.load_config()
|
|
234
|
+
|
|
235
|
+
# Only proceed if we have a valid model configuration
|
|
236
|
+
if model_name not in models_config:
|
|
237
|
+
raise ValueError(f"Model '{model_name}' not found in configuration")
|
|
238
|
+
|
|
239
|
+
model = ModelFactory.get_model(model_name, models_config)
|
|
240
|
+
|
|
241
|
+
# Create a temporary agent instance to avoid interfering with current agent state
|
|
242
|
+
instructions = agent_config.get_system_prompt()
|
|
243
|
+
temp_agent = Agent(
|
|
244
|
+
model=model,
|
|
245
|
+
instructions=instructions,
|
|
246
|
+
output_type=str,
|
|
247
|
+
retries=3,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# Register the tools that the agent needs
|
|
251
|
+
from code_puppy.tools import register_tools_for_agent
|
|
252
|
+
agent_tools = agent_config.get_available_tools()
|
|
253
|
+
register_tools_for_agent(temp_agent, agent_tools)
|
|
254
|
+
|
|
255
|
+
# Run the temporary agent with the provided prompt
|
|
256
|
+
result = temp_agent.run_sync(prompt)
|
|
257
|
+
|
|
258
|
+
# Extract the response from the result
|
|
259
|
+
response = result.output
|
|
260
|
+
|
|
261
|
+
emit_system_message(f"Response: {response}", message_group=group_id)
|
|
262
|
+
emit_divider(message_group=group_id)
|
|
263
|
+
|
|
264
|
+
return AgentInvokeOutput(response=response, agent_name=agent_name)
|
|
265
|
+
|
|
266
|
+
except Exception as e:
|
|
267
|
+
error_msg = f"Error invoking agent '{agent_name}': {str(e)}"
|
|
268
|
+
emit_error(error_msg, message_group=group_id)
|
|
269
|
+
emit_divider(message_group=group_id)
|
|
270
|
+
return AgentInvokeOutput(response=None, agent_name=agent_name, error=error_msg)
|
|
271
|
+
|
|
272
|
+
return invoke_agent
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
code_puppy/__init__.py,sha256=ehbM1-wMjNmOXk_DBhhJECFyBv2dRHwwo7ucjHeM68E,107
|
|
2
2
|
code_puppy/__main__.py,sha256=pDVssJOWP8A83iFkxMLY9YteHYat0EyWDQqMkKHpWp4,203
|
|
3
|
-
code_puppy/agent.py,sha256=
|
|
3
|
+
code_puppy/agent.py,sha256=ZQb1YWvBJXzGzXML1wI4gPf_-2kClMrYFleFMtA-9RA,6925
|
|
4
4
|
code_puppy/callbacks.py,sha256=6wYB6K_fGSCkKKEFaYOYkJT45WaV5W_NhUIzcvVH_nU,5060
|
|
5
5
|
code_puppy/config.py,sha256=9yWKHKjLJ2Ddl4frrBI9VRIwPvoWpIx1fAd1YpAvOSQ,15330
|
|
6
6
|
code_puppy/http_utils.py,sha256=BAvt4hed7fVMXglA7eS9gOb08h2YTuOyai6VmQq09fg,3432
|
|
7
|
-
code_puppy/main.py,sha256=
|
|
7
|
+
code_puppy/main.py,sha256=Vv5HSJnkgZhCvvOoXrJ2zqM5P-i47-RcYAU00Z1Pfx0,21733
|
|
8
8
|
code_puppy/message_history_processor.py,sha256=O2rKp7W6YeIg93W8b0XySTUEQgIZm0f_06--_kzHugM,16145
|
|
9
9
|
code_puppy/model_factory.py,sha256=NoG9wDTosaaDrFIGtq3oq8gDe0J_7N6CUKuesXz87qM,10878
|
|
10
10
|
code_puppy/models.json,sha256=dAfpMMI2EEeOMv0ynHSmMuJAYDLcZrs5gCLX3voC4-A,3252
|
|
@@ -15,12 +15,13 @@ code_puppy/summarization_agent.py,sha256=-e6yUGZ22ahSaF0y7QhgVcQBfx5ktNUkPxBIWQf
|
|
|
15
15
|
code_puppy/token_utils.py,sha256=inLo-S2YERGA-JmV-nrSFN7KMswSfHxpawAuK6YiDHc,1982
|
|
16
16
|
code_puppy/version_checker.py,sha256=bjLDmgGPrl7XnYwX1u13O8uFlsfikV90PK6nbA9Z9QU,1150
|
|
17
17
|
code_puppy/agents/__init__.py,sha256=SwtHGNG1GIgDBv7y3EGIXOXEWrG_Ou7fEknNgFbrHv8,594
|
|
18
|
-
code_puppy/agents/agent_code_puppy.py,sha256=
|
|
19
|
-
code_puppy/agents/agent_creator_agent.py,sha256=
|
|
18
|
+
code_puppy/agents/agent_code_puppy.py,sha256=At4PDmBsBFCrKKvdOKlCGa6XLu1PB23kXJb3SEHjT1I,7977
|
|
19
|
+
code_puppy/agents/agent_creator_agent.py,sha256=IUPLJDhhqIQ8NAjNTFQ_3Z5kZYYxVAUE0RPkcz90reY,20450
|
|
20
20
|
code_puppy/agents/agent_manager.py,sha256=nXvro6fpX8KA-NedRoVJuhJW966trrePOrH4eAnqq40,17034
|
|
21
|
+
code_puppy/agents/agent_orchestrator.json,sha256=Iqnc0p6ICoAlUTMkEsi1XXMXJi4pdxVnWZUMaih6s5o,1267
|
|
21
22
|
code_puppy/agents/base_agent.py,sha256=XYSff6IQX9Z6C7hPVbN_YXC2xfjwd268e2jtG3ZGnVk,3450
|
|
22
23
|
code_puppy/agents/json_agent.py,sha256=0j6_P1ppje7TsjaZIbxKn8meiuvoBngvjVLNdtCkGwc,4272
|
|
23
|
-
code_puppy/agents/runtime_manager.py,sha256=
|
|
24
|
+
code_puppy/agents/runtime_manager.py,sha256=t8F37mYX3txUqVoOrGwt0VR4Yc-M8AKIxSo22P0TOmg,9921
|
|
24
25
|
code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZv_YRmU,45
|
|
25
26
|
code_puppy/command_line/command_handler.py,sha256=1o9tKAGycpHFDBldYRAAvY5HJ6QAfikLPrXTEkfw37o,21137
|
|
26
27
|
code_puppy/command_line/file_path_completion.py,sha256=gw8NpIxa6GOpczUJRyh7VNZwoXKKn-yvCqit7h2y6Gg,2931
|
|
@@ -61,7 +62,7 @@ code_puppy/mcp/health_monitor.py,sha256=n5R6EeYOYbUucUFe74qGWCU3g6Mep5UEQbLF0wbT
|
|
|
61
62
|
code_puppy/mcp/managed_server.py,sha256=vYD_uwHJDyRB7iHNIZHTNEin5bVe9lCMMjJyFMWNjc8,14381
|
|
62
63
|
code_puppy/mcp/manager.py,sha256=Yx9zxukdXgdPDgeJiiQPYlPae0zQPofHWB-axuoMNc8,26426
|
|
63
64
|
code_puppy/mcp/registry.py,sha256=IvbIL-pETQ7HS7iRgsoT5j7eY8TOJXqYczSiigT2ofU,15752
|
|
64
|
-
code_puppy/mcp/retry_manager.py,sha256=
|
|
65
|
+
code_puppy/mcp/retry_manager.py,sha256=evVxbtrsHNyo8UoI7zpO-NVDegibn82RLlgN8VKewA8,10665
|
|
65
66
|
code_puppy/mcp/server_registry_catalog.py,sha256=jYx__H-b-f4Ucpczx-wwpU-TRx6fXzFDR0kr2Qhwqb0,38857
|
|
66
67
|
code_puppy/mcp/status_tracker.py,sha256=uekxrzkzIWrv3OfSVgblaPuoGFcAh_dBYwCcaHZ_CrM,12183
|
|
67
68
|
code_puppy/mcp/system_tools.py,sha256=7_oR8k0c8YjtCcYF9g7A946oAGuKOf_i-92aJH7VmlQ,7331
|
|
@@ -75,7 +76,8 @@ code_puppy/messaging/spinner/console_spinner.py,sha256=cuOXQH99dJ1cq0l_rpCLVCGNs
|
|
|
75
76
|
code_puppy/messaging/spinner/spinner_base.py,sha256=474qMrTYpNfWcprFzmhaOJEOC-2rRHpTFCLsnl54bXA,1689
|
|
76
77
|
code_puppy/messaging/spinner/textual_spinner.py,sha256=Omx9A-FSPkxYDMYgBXgYMBQnK-DMlyqLOgkFVG8cmo4,3465
|
|
77
78
|
code_puppy/plugins/__init__.py,sha256=fksDqMUiXPJ5WNuMsYsVR8ulueQRCXPlvECEyicHPtQ,1312
|
|
78
|
-
code_puppy/tools/__init__.py,sha256=
|
|
79
|
+
code_puppy/tools/__init__.py,sha256=YiiXRqxU1BEJ5t0Oe163lSqOneI9sKtwDW0swCPgBt4,2119
|
|
80
|
+
code_puppy/tools/agent_tools.py,sha256=tG11PMmjuU-pYG1MFCgqsYiC1Q8C-zPsitAYXxl3mRA,9726
|
|
79
81
|
code_puppy/tools/command_runner.py,sha256=GVNsgwjTFD9tkNlycgMNmMoVPdmMkZkbAcHH5y6iMww,26070
|
|
80
82
|
code_puppy/tools/common.py,sha256=pL-9xcRs3rxU7Fl9X9EUgbDp2-csh2LLJ5DHH_KAHKY,10596
|
|
81
83
|
code_puppy/tools/file_modifications.py,sha256=oeNEQItqwMhGOeEN2TzGR7TjmgLsfFFdPaVMzWbfXIQ,30398
|
|
@@ -123,9 +125,9 @@ code_puppy/tui/tests/test_sidebar_history_navigation.py,sha256=JGiyua8A2B8dLfwiE
|
|
|
123
125
|
code_puppy/tui/tests/test_status_bar.py,sha256=nYT_FZGdmqnnbn6o0ZuOkLtNUtJzLSmtX8P72liQ5Vo,1797
|
|
124
126
|
code_puppy/tui/tests/test_timestamped_history.py,sha256=nVXt9hExZZ_8MFP-AZj4L4bB_1Eo_mc-ZhVICzTuw3I,1799
|
|
125
127
|
code_puppy/tui/tests/test_tools.py,sha256=kgzzAkK4r0DPzQwHHD4cePpVNgrHor6cFr05Pg6DBWg,2687
|
|
126
|
-
code_puppy-0.0.
|
|
127
|
-
code_puppy-0.0.
|
|
128
|
-
code_puppy-0.0.
|
|
129
|
-
code_puppy-0.0.
|
|
130
|
-
code_puppy-0.0.
|
|
131
|
-
code_puppy-0.0.
|
|
128
|
+
code_puppy-0.0.142.data/data/code_puppy/models.json,sha256=dAfpMMI2EEeOMv0ynHSmMuJAYDLcZrs5gCLX3voC4-A,3252
|
|
129
|
+
code_puppy-0.0.142.dist-info/METADATA,sha256=NbPyMrnJMdLPcVGsRVp6EF9rvRyWDRi7G7kDfRQrmJI,19873
|
|
130
|
+
code_puppy-0.0.142.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
131
|
+
code_puppy-0.0.142.dist-info/entry_points.txt,sha256=d8YkBvIUxF-dHNJAj-x4fPEqizbY5d_TwvYpc01U5kw,58
|
|
132
|
+
code_puppy-0.0.142.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
|
|
133
|
+
code_puppy-0.0.142.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|