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.
- code_puppy/agents/__init__.py +2 -2
- code_puppy/agents/agent_code_puppy.py +2 -1
- code_puppy/agents/agent_creator_agent.py +3 -2
- code_puppy/agents/agent_golang_reviewer.py +61 -0
- code_puppy/agents/agent_manager.py +5 -5
- code_puppy/agents/base_agent.py +60 -40
- code_puppy/command_line/command_handler.py +19 -10
- code_puppy/command_line/mcp/start_all_command.py +1 -1
- code_puppy/command_line/mcp/start_command.py +0 -1
- code_puppy/command_line/mcp/stop_all_command.py +1 -1
- code_puppy/command_line/mcp/stop_command.py +1 -0
- code_puppy/config.py +5 -3
- code_puppy/main.py +5 -2
- code_puppy/mcp_/examples/retry_example.py +4 -1
- code_puppy/messaging/spinner/console_spinner.py +1 -1
- code_puppy/model_factory.py +1 -1
- code_puppy/round_robin_model.py +2 -4
- code_puppy/tools/agent_tools.py +10 -8
- code_puppy/tools/browser/browser_screenshot.py +4 -3
- code_puppy/tools/browser/browser_scripts.py +0 -6
- code_puppy/tools/browser/browser_workflows.py +28 -20
- code_puppy/tools/browser/camoufox_manager.py +10 -9
- code_puppy/tools/browser_scripts.py +0 -6
- code_puppy/tools/browser_workflows.py +28 -20
- code_puppy/tools/command_runner.py +1 -1
- code_puppy/tui/app.py +3 -13
- code_puppy/tui/components/chat_view.py +1 -0
- code_puppy/tui/screens/settings.py +3 -3
- {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/METADATA +10 -10
- {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/RECORD +34 -33
- {code_puppy-0.0.176.data → code_puppy-0.0.178.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.176.dist-info → code_puppy-0.0.178.dist-info}/licenses/LICENSE +0 -0
code_puppy/round_robin_model.py
CHANGED
|
@@ -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:
|
code_puppy/tools/agent_tools.py
CHANGED
|
@@ -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.
|
|
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
|
|
16
|
+
from .vqa_agent import run_vqa_analysis
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
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 (
|
|
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(
|
|
38
|
-
safe_name +=
|
|
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,
|
|
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(
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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(
|
|
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[
|
|
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(
|
|
129
|
-
name +=
|
|
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 {
|
|
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,
|
|
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
|
|
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(
|
|
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 (
|
|
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(
|
|
38
|
-
safe_name +=
|
|
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,
|
|
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(
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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(
|
|
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[
|
|
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(
|
|
129
|
-
name +=
|
|
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 {
|
|
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,
|
|
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)}")
|
|
@@ -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.
|
|
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
|
-
|
|
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",
|