devduck 0.4.0__py3-none-any.whl → 0.4.1__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.
Potentially problematic release.
This version of devduck might be problematic. Click here for more details.
- devduck/__init__.py +31 -180
- devduck/_version.py +2 -2
- devduck/tools/system_prompt.py +485 -0
- {devduck-0.4.0.dist-info → devduck-0.4.1.dist-info}/METADATA +29 -6
- {devduck-0.4.0.dist-info → devduck-0.4.1.dist-info}/RECORD +9 -8
- {devduck-0.4.0.dist-info → devduck-0.4.1.dist-info}/WHEEL +0 -0
- {devduck-0.4.0.dist-info → devduck-0.4.1.dist-info}/entry_points.txt +0 -0
- {devduck-0.4.0.dist-info → devduck-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {devduck-0.4.0.dist-info → devduck-0.4.1.dist-info}/top_level.txt +0 -0
devduck/__init__.py
CHANGED
|
@@ -126,168 +126,6 @@ def get_own_source_code():
|
|
|
126
126
|
except Exception as e:
|
|
127
127
|
return f"Error reading own source code: {e}"
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
# 🛠️ System prompt tool (with .prompt file persistence)
|
|
131
|
-
def system_prompt_tool(
|
|
132
|
-
action: str,
|
|
133
|
-
prompt: str | None = None,
|
|
134
|
-
context: str | None = None,
|
|
135
|
-
variable_name: str = "SYSTEM_PROMPT",
|
|
136
|
-
) -> Dict[str, Any]:
|
|
137
|
-
"""
|
|
138
|
-
Manage the agent's system prompt dynamically with file persistence.
|
|
139
|
-
|
|
140
|
-
Args:
|
|
141
|
-
action: "view", "update", "add_context", or "reset"
|
|
142
|
-
prompt: New system prompt text (required for "update")
|
|
143
|
-
context: Additional context to prepend (for "add_context")
|
|
144
|
-
variable_name: Environment variable name (default: SYSTEM_PROMPT)
|
|
145
|
-
|
|
146
|
-
Returns:
|
|
147
|
-
Dict with status and content
|
|
148
|
-
"""
|
|
149
|
-
from pathlib import Path
|
|
150
|
-
import tempfile
|
|
151
|
-
|
|
152
|
-
def _get_prompt_file_path() -> Path:
|
|
153
|
-
"""Get the .prompt file path in temp directory."""
|
|
154
|
-
temp_dir = Path(tempfile.gettempdir()) / ".devduck"
|
|
155
|
-
temp_dir.mkdir(exist_ok=True, mode=0o700) # Create with restrictive permissions
|
|
156
|
-
return temp_dir / ".prompt"
|
|
157
|
-
|
|
158
|
-
def _write_prompt_file(prompt_text: str) -> None:
|
|
159
|
-
"""Write prompt to .prompt file in temp directory."""
|
|
160
|
-
prompt_file = _get_prompt_file_path()
|
|
161
|
-
try:
|
|
162
|
-
# Create file with restrictive permissions
|
|
163
|
-
with open(
|
|
164
|
-
prompt_file,
|
|
165
|
-
"w",
|
|
166
|
-
encoding="utf-8",
|
|
167
|
-
opener=lambda path, flags: os.open(path, flags, 0o600),
|
|
168
|
-
) as f:
|
|
169
|
-
f.write(prompt_text)
|
|
170
|
-
except (OSError, PermissionError):
|
|
171
|
-
try:
|
|
172
|
-
prompt_file.write_text(prompt_text, encoding="utf-8")
|
|
173
|
-
prompt_file.chmod(0o600)
|
|
174
|
-
except (OSError, PermissionError):
|
|
175
|
-
prompt_file.write_text(prompt_text, encoding="utf-8")
|
|
176
|
-
|
|
177
|
-
def _get_system_prompt(var_name: str) -> str:
|
|
178
|
-
"""Get current system prompt from environment variable."""
|
|
179
|
-
return os.environ.get(var_name, "")
|
|
180
|
-
|
|
181
|
-
def _update_system_prompt(new_prompt: str, var_name: str) -> None:
|
|
182
|
-
"""Update system prompt in both environment and .prompt file."""
|
|
183
|
-
os.environ[var_name] = new_prompt
|
|
184
|
-
if var_name == "SYSTEM_PROMPT":
|
|
185
|
-
_write_prompt_file(new_prompt)
|
|
186
|
-
|
|
187
|
-
try:
|
|
188
|
-
if action == "view":
|
|
189
|
-
current = _get_system_prompt(variable_name)
|
|
190
|
-
return {
|
|
191
|
-
"status": "success",
|
|
192
|
-
"content": [
|
|
193
|
-
{"text": f"Current system prompt from {variable_name}:{current}"}
|
|
194
|
-
],
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
elif action == "update":
|
|
198
|
-
if not prompt:
|
|
199
|
-
return {
|
|
200
|
-
"status": "error",
|
|
201
|
-
"content": [
|
|
202
|
-
{"text": "Error: prompt parameter required for update action"}
|
|
203
|
-
],
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
_update_system_prompt(prompt, variable_name)
|
|
207
|
-
|
|
208
|
-
if variable_name == "SYSTEM_PROMPT":
|
|
209
|
-
message = f"System prompt updated (env: {variable_name}, file: .prompt)"
|
|
210
|
-
else:
|
|
211
|
-
message = f"System prompt updated (env: {variable_name})"
|
|
212
|
-
|
|
213
|
-
return {"status": "success", "content": [{"text": message}]}
|
|
214
|
-
|
|
215
|
-
elif action == "add_context":
|
|
216
|
-
if not context:
|
|
217
|
-
return {
|
|
218
|
-
"status": "error",
|
|
219
|
-
"content": [
|
|
220
|
-
{
|
|
221
|
-
"text": "Error: context parameter required for add_context action"
|
|
222
|
-
}
|
|
223
|
-
],
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
current = _get_system_prompt(variable_name)
|
|
227
|
-
new_prompt = f"{current} {context}" if current else context
|
|
228
|
-
_update_system_prompt(new_prompt, variable_name)
|
|
229
|
-
|
|
230
|
-
if variable_name == "SYSTEM_PROMPT":
|
|
231
|
-
message = f"Context added to system prompt (env: {variable_name}, file: .prompt)"
|
|
232
|
-
else:
|
|
233
|
-
message = f"Context added to system prompt (env: {variable_name})"
|
|
234
|
-
|
|
235
|
-
return {"status": "success", "content": [{"text": message}]}
|
|
236
|
-
|
|
237
|
-
elif action == "reset":
|
|
238
|
-
os.environ.pop(variable_name, None)
|
|
239
|
-
|
|
240
|
-
if variable_name == "SYSTEM_PROMPT":
|
|
241
|
-
prompt_file = _get_prompt_file_path()
|
|
242
|
-
if prompt_file.exists():
|
|
243
|
-
try:
|
|
244
|
-
prompt_file.unlink()
|
|
245
|
-
except (OSError, PermissionError):
|
|
246
|
-
pass
|
|
247
|
-
message = (
|
|
248
|
-
f"System prompt reset (env: {variable_name}, file: .prompt cleared)"
|
|
249
|
-
)
|
|
250
|
-
else:
|
|
251
|
-
message = f"System prompt reset (env: {variable_name})"
|
|
252
|
-
|
|
253
|
-
return {"status": "success", "content": [{"text": message}]}
|
|
254
|
-
|
|
255
|
-
elif action == "get":
|
|
256
|
-
# Backward compatibility
|
|
257
|
-
current = _get_system_prompt(variable_name)
|
|
258
|
-
return {
|
|
259
|
-
"status": "success",
|
|
260
|
-
"content": [{"text": f"System prompt: {current}"}],
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
elif action == "set":
|
|
264
|
-
# Backward compatibility
|
|
265
|
-
if prompt is None:
|
|
266
|
-
return {"status": "error", "content": [{"text": "No prompt provided"}]}
|
|
267
|
-
|
|
268
|
-
if context:
|
|
269
|
-
prompt = f"{context} {prompt}"
|
|
270
|
-
|
|
271
|
-
_update_system_prompt(prompt, variable_name)
|
|
272
|
-
return {
|
|
273
|
-
"status": "success",
|
|
274
|
-
"content": [{"text": "System prompt updated successfully"}],
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
else:
|
|
278
|
-
return {
|
|
279
|
-
"status": "error",
|
|
280
|
-
"content": [
|
|
281
|
-
{
|
|
282
|
-
"text": f"Unknown action '{action}'. Valid: view, update, add_context, reset"
|
|
283
|
-
}
|
|
284
|
-
],
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
except Exception as e:
|
|
288
|
-
return {"status": "error", "content": [{"text": f"Error: {str(e)}"}]}
|
|
289
|
-
|
|
290
|
-
|
|
291
129
|
def view_logs_tool(
|
|
292
130
|
action: str = "view",
|
|
293
131
|
lines: int = 100,
|
|
@@ -661,6 +499,7 @@ class DevDuck:
|
|
|
661
499
|
use_github,
|
|
662
500
|
create_subagent,
|
|
663
501
|
store_in_kb,
|
|
502
|
+
system_prompt,
|
|
664
503
|
)
|
|
665
504
|
|
|
666
505
|
core_tools.extend(
|
|
@@ -672,6 +511,7 @@ class DevDuck:
|
|
|
672
511
|
use_github,
|
|
673
512
|
create_subagent,
|
|
674
513
|
store_in_kb,
|
|
514
|
+
system_prompt
|
|
675
515
|
]
|
|
676
516
|
)
|
|
677
517
|
except ImportError as e:
|
|
@@ -731,17 +571,6 @@ class DevDuck:
|
|
|
731
571
|
"strands-agents-tools not installed - core tools unavailable (install with: pip install devduck[all])"
|
|
732
572
|
)
|
|
733
573
|
|
|
734
|
-
# Wrap system_prompt_tool with @tool decorator
|
|
735
|
-
@tool
|
|
736
|
-
def system_prompt(
|
|
737
|
-
action: str,
|
|
738
|
-
prompt: str = None,
|
|
739
|
-
context: str = None,
|
|
740
|
-
variable_name: str = "SYSTEM_PROMPT",
|
|
741
|
-
) -> Dict[str, Any]:
|
|
742
|
-
"""Manage agent system prompt dynamically."""
|
|
743
|
-
return system_prompt_tool(action, prompt, context, variable_name)
|
|
744
|
-
|
|
745
574
|
# Wrap view_logs_tool with @tool decorator
|
|
746
575
|
@tool
|
|
747
576
|
def view_logs(
|
|
@@ -753,7 +582,7 @@ class DevDuck:
|
|
|
753
582
|
return view_logs_tool(action, lines, pattern)
|
|
754
583
|
|
|
755
584
|
# Add built-in tools to the toolset
|
|
756
|
-
core_tools.extend([
|
|
585
|
+
core_tools.extend([view_logs])
|
|
757
586
|
|
|
758
587
|
# Assign tools
|
|
759
588
|
self.tools = core_tools
|
|
@@ -980,9 +809,20 @@ def weather(action: str, location: str = None) -> Dict[str, Any]:
|
|
|
980
809
|
```
|
|
981
810
|
|
|
982
811
|
## System Prompt Management:
|
|
983
|
-
-
|
|
984
|
-
-
|
|
985
|
-
-
|
|
812
|
+
- **View**: system_prompt(action='view') - See current prompt
|
|
813
|
+
- **Update Local**: system_prompt(action='update', prompt='new text') - Updates env var + .prompt file
|
|
814
|
+
- **Update GitHub**: system_prompt(action='update', prompt='text', repository='cagataycali/devduck') - Syncs to repo variables
|
|
815
|
+
- **Variable Name**: system_prompt(action='update', prompt='text', variable_name='CUSTOM_PROMPT') - Use custom var
|
|
816
|
+
- **Add Context**: system_prompt(action='add_context', context='new learning') - Append without replacing
|
|
817
|
+
|
|
818
|
+
### 🧠 Self-Improvement Pattern:
|
|
819
|
+
When you learn something valuable during conversations:
|
|
820
|
+
1. Identify the new insight or pattern
|
|
821
|
+
2. Use system_prompt(action='add_context', context='...') to append it
|
|
822
|
+
3. Sync to GitHub: system_prompt(action='update', prompt=new_full_prompt, repository='owner/repo')
|
|
823
|
+
4. New learnings persist across sessions via SYSTEM_PROMPT env var
|
|
824
|
+
|
|
825
|
+
**Repository Integration**: Set repository='cagataycali/devduck' to sync prompts across deployments
|
|
986
826
|
|
|
987
827
|
## Shell Commands:
|
|
988
828
|
- Prefix with ! to execute shell commands directly
|
|
@@ -1520,9 +1360,20 @@ You have full access to your own source code for self-awareness and self-modific
|
|
|
1520
1360
|
- Full bidirectional communication
|
|
1521
1361
|
|
|
1522
1362
|
## System Prompt Management:
|
|
1523
|
-
-
|
|
1524
|
-
-
|
|
1525
|
-
-
|
|
1363
|
+
- **View**: system_prompt(action='view') - See current prompt
|
|
1364
|
+
- **Update Local**: system_prompt(action='update', prompt='new text') - Updates env var + .prompt file
|
|
1365
|
+
- **Update GitHub**: system_prompt(action='update', prompt='text', repository='cagataycali/devduck') - Syncs to repo variables
|
|
1366
|
+
- **Variable Name**: system_prompt(action='update', prompt='text', variable_name='CUSTOM_PROMPT') - Use custom var
|
|
1367
|
+
- **Add Context**: system_prompt(action='add_context', context='new learning') - Append without replacing
|
|
1368
|
+
|
|
1369
|
+
### 🧠 Self-Improvement Pattern:
|
|
1370
|
+
When you learn something valuable during conversations:
|
|
1371
|
+
1. Identify the new insight or pattern
|
|
1372
|
+
2. Use system_prompt(action='add_context', context='...') to append it
|
|
1373
|
+
3. Optionally sync to GitHub: system_prompt(action='update', prompt=new_full_prompt, repository='owner/repo')
|
|
1374
|
+
4. New learnings persist across sessions via SYSTEM_PROMPT env var
|
|
1375
|
+
|
|
1376
|
+
**Repository Integration**: Set repository='cagataycali/devduck' to sync prompts across deployments
|
|
1526
1377
|
|
|
1527
1378
|
## Shell Commands:
|
|
1528
1379
|
- Prefix with ! to execute shell commands directly
|
devduck/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.4.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 4,
|
|
31
|
+
__version__ = version = '0.4.1'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 4, 1)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
"""System prompt management tool for Strands Agents.
|
|
2
|
+
|
|
3
|
+
This module provides a tool to view and modify system prompts used by the agent.
|
|
4
|
+
It helps with dynamic adaptation of the agent's behavior and capabilities,
|
|
5
|
+
and can persist changes by updating GitHub repository variables.
|
|
6
|
+
|
|
7
|
+
Key Features:
|
|
8
|
+
1. View current system prompt from any environment variable
|
|
9
|
+
2. Update system prompt (in-memory and GitHub repository variable)
|
|
10
|
+
3. Add context information to system prompt
|
|
11
|
+
4. Reset system prompt to default
|
|
12
|
+
5. Support for custom variable names (SYSTEM_PROMPT, TOOL_BUILDER_SYSTEM_PROMPT, etc.)
|
|
13
|
+
|
|
14
|
+
Usage Examples:
|
|
15
|
+
```python
|
|
16
|
+
from strands import Agent
|
|
17
|
+
from protocol_tools import system_prompt
|
|
18
|
+
|
|
19
|
+
agent = Agent(tools=[system_prompt])
|
|
20
|
+
|
|
21
|
+
# View current system prompt (default SYSTEM_PROMPT variable)
|
|
22
|
+
result = agent.tool.system_prompt(action="view")
|
|
23
|
+
|
|
24
|
+
# Update system prompt for tool builder
|
|
25
|
+
result = agent.tool.system_prompt(
|
|
26
|
+
action="update",
|
|
27
|
+
prompt="You are a specialized tool builder agent...",
|
|
28
|
+
repository="owner/repo",
|
|
29
|
+
variable_name="TOOL_BUILDER_SYSTEM_PROMPT",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Work with any custom variable name
|
|
33
|
+
result = agent.tool.system_prompt(
|
|
34
|
+
action="view", variable_name="MY_CUSTOM_PROMPT"
|
|
35
|
+
)
|
|
36
|
+
```
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
import os
|
|
40
|
+
from typing import Any
|
|
41
|
+
|
|
42
|
+
import requests
|
|
43
|
+
from strands import tool
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get_github_token() -> str:
|
|
47
|
+
"""Get GitHub token from environment variable."""
|
|
48
|
+
return os.environ.get("PAT_TOKEN", os.environ.get("GITHUB_TOKEN", ""))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _get_github_repository_variable(
|
|
52
|
+
repository: str, name: str, token: str
|
|
53
|
+
) -> dict[str, Any]:
|
|
54
|
+
"""Fetch a GitHub repository variable.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
repository: The repository in format "owner/repo"
|
|
58
|
+
name: The variable name
|
|
59
|
+
token: GitHub token
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Dictionary with success status, message, and value if successful
|
|
63
|
+
"""
|
|
64
|
+
# GitHub API endpoint for repository variables
|
|
65
|
+
url = f"https://api.github.com/repos/{repository}/actions/variables/{name}"
|
|
66
|
+
|
|
67
|
+
headers = {
|
|
68
|
+
"Accept": "application/vnd.github+json",
|
|
69
|
+
"Authorization": f"Bearer {token}",
|
|
70
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
response = requests.get(url, headers=headers, timeout=30)
|
|
75
|
+
|
|
76
|
+
if response.status_code == 200:
|
|
77
|
+
data = response.json()
|
|
78
|
+
return {
|
|
79
|
+
"success": True,
|
|
80
|
+
"message": f"Variable {name} fetched successfully",
|
|
81
|
+
"value": data.get("value", ""),
|
|
82
|
+
}
|
|
83
|
+
else:
|
|
84
|
+
error_message = (
|
|
85
|
+
f"Failed to fetch variable: {response.status_code} - {response.text}"
|
|
86
|
+
)
|
|
87
|
+
return {"success": False, "message": error_message, "value": ""}
|
|
88
|
+
except Exception as e:
|
|
89
|
+
return {
|
|
90
|
+
"success": False,
|
|
91
|
+
"message": f"Error fetching GitHub variable: {e!s}",
|
|
92
|
+
"value": "",
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _get_system_prompt(
|
|
97
|
+
repository: str | None = None, variable_name: str = "SYSTEM_PROMPT"
|
|
98
|
+
) -> str:
|
|
99
|
+
"""Get the current system prompt.
|
|
100
|
+
|
|
101
|
+
First checks the local environment variable.
|
|
102
|
+
If empty and repository is provided, tries to fetch from GitHub repository variables.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
repository: Optional GitHub repository in format "owner/repo"
|
|
106
|
+
variable_name: Name of the environment/repository variable to use
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
The system prompt string
|
|
110
|
+
"""
|
|
111
|
+
# First check local environment
|
|
112
|
+
local_prompt = os.environ.get(variable_name, "")
|
|
113
|
+
if local_prompt:
|
|
114
|
+
return local_prompt
|
|
115
|
+
|
|
116
|
+
# If local is empty and repository is provided, try GitHub
|
|
117
|
+
if repository and not local_prompt:
|
|
118
|
+
token = _get_github_token()
|
|
119
|
+
if token:
|
|
120
|
+
result = _get_github_repository_variable(
|
|
121
|
+
repository=repository, name=variable_name, token=token
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if result["success"]:
|
|
125
|
+
# Store in local environment for future use
|
|
126
|
+
if result["value"]:
|
|
127
|
+
os.environ[variable_name] = result["value"]
|
|
128
|
+
return str(result["value"])
|
|
129
|
+
|
|
130
|
+
# Default to empty string if nothing found
|
|
131
|
+
return ""
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _update_system_prompt(
|
|
135
|
+
new_prompt: str, variable_name: str = "SYSTEM_PROMPT"
|
|
136
|
+
) -> None:
|
|
137
|
+
"""Update the system prompt in the environment variable."""
|
|
138
|
+
os.environ[variable_name] = new_prompt
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _get_github_event_context() -> str:
|
|
142
|
+
"""Get GitHub event context information from environment variables."""
|
|
143
|
+
event_context = []
|
|
144
|
+
|
|
145
|
+
# GitHub repository information
|
|
146
|
+
repo = os.environ.get("GITHUB_REPOSITORY", "")
|
|
147
|
+
if repo:
|
|
148
|
+
event_context.append(f"Repository: {repo}")
|
|
149
|
+
|
|
150
|
+
# Event type
|
|
151
|
+
event_name = os.environ.get("GITHUB_EVENT_NAME", "")
|
|
152
|
+
if event_name:
|
|
153
|
+
event_context.append(f"Event Type: {event_name}")
|
|
154
|
+
|
|
155
|
+
# Actor
|
|
156
|
+
actor = os.environ.get("GITHUB_ACTOR", "")
|
|
157
|
+
if actor:
|
|
158
|
+
event_context.append(f"Actor: {actor}")
|
|
159
|
+
|
|
160
|
+
# Add more GitHub context variables as needed
|
|
161
|
+
return "\n".join(event_context)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _update_github_repository_variable(
|
|
165
|
+
repository: str, name: str, value: str, token: str
|
|
166
|
+
) -> dict[str, Any]:
|
|
167
|
+
"""Update a GitHub repository variable.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
repository: The repository in format "owner/repo"
|
|
171
|
+
name: The variable name
|
|
172
|
+
value: The variable value
|
|
173
|
+
token: GitHub token
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Dictionary with status and message
|
|
177
|
+
"""
|
|
178
|
+
# GitHub API endpoint for repository variables
|
|
179
|
+
url = f"https://api.github.com/repos/{repository}/actions/variables/{name}"
|
|
180
|
+
|
|
181
|
+
headers = {
|
|
182
|
+
"Accept": "application/vnd.github+json",
|
|
183
|
+
"Authorization": f"Bearer {token}",
|
|
184
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
data = {"name": name, "value": value}
|
|
188
|
+
|
|
189
|
+
response = requests.patch(url, headers=headers, json=data, timeout=30)
|
|
190
|
+
|
|
191
|
+
if response.status_code == 204:
|
|
192
|
+
return {"success": True, "message": f"Variable {name} updated successfully"}
|
|
193
|
+
else:
|
|
194
|
+
error_message = (
|
|
195
|
+
f"Failed to update variable: {response.status_code} - {response.text}"
|
|
196
|
+
)
|
|
197
|
+
return {"success": False, "message": error_message}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@tool
|
|
201
|
+
def system_prompt(
|
|
202
|
+
action: str,
|
|
203
|
+
prompt: str | None = None,
|
|
204
|
+
context: str | None = None,
|
|
205
|
+
repository: str | None = None,
|
|
206
|
+
variable_name: str = "SYSTEM_PROMPT",
|
|
207
|
+
) -> dict[str, str | list[dict[str, str]]]:
|
|
208
|
+
"""Manage the agent's system prompt.
|
|
209
|
+
|
|
210
|
+
This tool allows viewing and modifying the system prompt used by the agent.
|
|
211
|
+
It can be used to adapt the agent's behavior dynamically during runtime
|
|
212
|
+
and can update GitHub repository variables to persist changes.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
action: The action to perform on the system prompt. One of:
|
|
216
|
+
- "view": View the current system prompt
|
|
217
|
+
- "update": Replace the current system prompt
|
|
218
|
+
- "add_context": Add additional context to the system prompt
|
|
219
|
+
- "reset": Reset to default (empty or environment-defined)
|
|
220
|
+
- "get_github_context": Get GitHub event context
|
|
221
|
+
prompt: New system prompt when using the "update" action
|
|
222
|
+
context: Additional context to add when using the "add_context" action
|
|
223
|
+
repository: GitHub repository in format "owner/repo" to update repository
|
|
224
|
+
variable (e.g., "cagataycali/report-agent")
|
|
225
|
+
variable_name: Name of the environment/repository variable to use
|
|
226
|
+
(default: "SYSTEM_PROMPT")
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
A dictionary with the operation status and current system prompt
|
|
230
|
+
|
|
231
|
+
Example:
|
|
232
|
+
```python
|
|
233
|
+
# View current system prompt
|
|
234
|
+
result = system_prompt(action="view")
|
|
235
|
+
|
|
236
|
+
# Update system prompt in memory
|
|
237
|
+
result = system_prompt(
|
|
238
|
+
action="update", prompt="You are a specialized agent for task X..."
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Update GitHub repository variable
|
|
242
|
+
result = system_prompt(
|
|
243
|
+
action="update",
|
|
244
|
+
prompt="You are a specialized agent for task X...",
|
|
245
|
+
repository="owner/repo",
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Work with custom variable name
|
|
249
|
+
result = system_prompt(
|
|
250
|
+
action="update",
|
|
251
|
+
prompt="You are a tool builder...",
|
|
252
|
+
repository="owner/repo",
|
|
253
|
+
variable_name="TOOL_BUILDER_SYSTEM_PROMPT",
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
"""
|
|
257
|
+
try:
|
|
258
|
+
if action == "view":
|
|
259
|
+
current_prompt = _get_system_prompt(repository, variable_name)
|
|
260
|
+
source = "local environment"
|
|
261
|
+
|
|
262
|
+
if not os.environ.get(variable_name) and repository:
|
|
263
|
+
source = f"GitHub repository {repository}"
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
"status": "success",
|
|
267
|
+
"content": [
|
|
268
|
+
{
|
|
269
|
+
"text": f"Current system prompt from {variable_name} (from {source}):\n\n{current_prompt}"
|
|
270
|
+
}
|
|
271
|
+
],
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
elif action == "update":
|
|
275
|
+
if not prompt:
|
|
276
|
+
return {
|
|
277
|
+
"status": "error",
|
|
278
|
+
"content": [
|
|
279
|
+
{
|
|
280
|
+
"text": "Error: prompt parameter is required for the update action"
|
|
281
|
+
}
|
|
282
|
+
],
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# Update in-memory environment variable
|
|
286
|
+
_update_system_prompt(prompt, variable_name)
|
|
287
|
+
|
|
288
|
+
# If repository is specified, also update GitHub repository variable
|
|
289
|
+
if repository:
|
|
290
|
+
token = _get_github_token()
|
|
291
|
+
if not token:
|
|
292
|
+
return {
|
|
293
|
+
"status": "error",
|
|
294
|
+
"content": [
|
|
295
|
+
{
|
|
296
|
+
"text": "Error: GitHub token not available. Cannot update repository variable."
|
|
297
|
+
}
|
|
298
|
+
],
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
result = _update_github_repository_variable(
|
|
302
|
+
repository=repository, name=variable_name, value=prompt, token=token
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
if result["success"]:
|
|
306
|
+
return {
|
|
307
|
+
"status": "success",
|
|
308
|
+
"content": [
|
|
309
|
+
{
|
|
310
|
+
"text": f"System prompt updated successfully in memory ({variable_name})"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"text": f"GitHub repository variable updated: {result['message']}"
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
}
|
|
317
|
+
else:
|
|
318
|
+
return {
|
|
319
|
+
"status": "error",
|
|
320
|
+
"content": [
|
|
321
|
+
{
|
|
322
|
+
"text": f"System prompt updated successfully in memory ({variable_name})"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"text": f"GitHub repository variable update failed: {result['message']}"
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
"status": "success",
|
|
332
|
+
"content": [
|
|
333
|
+
{
|
|
334
|
+
"text": f"System prompt updated successfully in memory ({variable_name})"
|
|
335
|
+
}
|
|
336
|
+
],
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
elif action == "add_context":
|
|
340
|
+
if not context:
|
|
341
|
+
return {
|
|
342
|
+
"status": "error",
|
|
343
|
+
"content": [
|
|
344
|
+
{
|
|
345
|
+
"text": "Error: context parameter is required for the add_context action"
|
|
346
|
+
}
|
|
347
|
+
],
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
current_prompt = _get_system_prompt(repository, variable_name)
|
|
351
|
+
new_prompt = f"{current_prompt}\n\n{context}" if current_prompt else context
|
|
352
|
+
_update_system_prompt(new_prompt, variable_name)
|
|
353
|
+
|
|
354
|
+
# If repository is specified, also update GitHub repository variable
|
|
355
|
+
if repository:
|
|
356
|
+
token = _get_github_token()
|
|
357
|
+
if not token:
|
|
358
|
+
return {
|
|
359
|
+
"status": "error",
|
|
360
|
+
"content": [
|
|
361
|
+
{
|
|
362
|
+
"text": f"Context added to system prompt successfully in memory ({variable_name})"
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"text": "Error: GitHub token not available. Cannot update repository variable."
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
result = _update_github_repository_variable(
|
|
371
|
+
repository=repository,
|
|
372
|
+
name=variable_name,
|
|
373
|
+
value=new_prompt,
|
|
374
|
+
token=token,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
if result["success"]:
|
|
378
|
+
return {
|
|
379
|
+
"status": "success",
|
|
380
|
+
"content": [
|
|
381
|
+
{
|
|
382
|
+
"text": f"Context added to system prompt successfully in memory ({variable_name})"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"text": f"GitHub repository variable updated: {result['message']}"
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
}
|
|
389
|
+
else:
|
|
390
|
+
return {
|
|
391
|
+
"status": "error",
|
|
392
|
+
"content": [
|
|
393
|
+
{
|
|
394
|
+
"text": f"Context added to system prompt successfully in memory ({variable_name})"
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
"text": f"GitHub repository variable update failed: {result['message']}"
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
"status": "success",
|
|
404
|
+
"content": [
|
|
405
|
+
{
|
|
406
|
+
"text": f"Context added to system prompt successfully ({variable_name})"
|
|
407
|
+
}
|
|
408
|
+
],
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
elif action == "reset":
|
|
412
|
+
# Reset to empty or environment-defined default
|
|
413
|
+
os.environ.pop(variable_name, None)
|
|
414
|
+
|
|
415
|
+
# If repository is specified, reset GitHub repository variable
|
|
416
|
+
if repository:
|
|
417
|
+
token = _get_github_token()
|
|
418
|
+
if not token:
|
|
419
|
+
return {
|
|
420
|
+
"status": "error",
|
|
421
|
+
"content": [
|
|
422
|
+
{
|
|
423
|
+
"text": f"System prompt reset to default in memory ({variable_name})"
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
"text": "Error: GitHub token not available. Cannot update repository variable."
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
result = _update_github_repository_variable(
|
|
432
|
+
repository=repository, name=variable_name, value="", token=token
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
if result["success"]:
|
|
436
|
+
return {
|
|
437
|
+
"status": "success",
|
|
438
|
+
"content": [
|
|
439
|
+
{
|
|
440
|
+
"text": f"System prompt reset to default in memory ({variable_name})"
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
"text": f"GitHub repository variable reset: {result['message']}"
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
}
|
|
447
|
+
else:
|
|
448
|
+
return {
|
|
449
|
+
"status": "error",
|
|
450
|
+
"content": [
|
|
451
|
+
{
|
|
452
|
+
"text": f"System prompt reset to default in memory ({variable_name})"
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"text": f"GitHub repository variable reset failed: {result['message']}"
|
|
456
|
+
},
|
|
457
|
+
],
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return {
|
|
461
|
+
"status": "success",
|
|
462
|
+
"content": [
|
|
463
|
+
{"text": f"System prompt reset to default ({variable_name})"}
|
|
464
|
+
],
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
elif action == "get_github_context":
|
|
468
|
+
github_context = _get_github_event_context()
|
|
469
|
+
return {
|
|
470
|
+
"status": "success",
|
|
471
|
+
"content": [{"text": f"GitHub Event Context:\n\n{github_context}"}],
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
else:
|
|
475
|
+
return {
|
|
476
|
+
"status": "error",
|
|
477
|
+
"content": [
|
|
478
|
+
{
|
|
479
|
+
"text": f"Error: Unknown action '{action}'. Valid actions are view, update, add_context, reset, get_github_context"
|
|
480
|
+
}
|
|
481
|
+
],
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
except Exception as e:
|
|
485
|
+
return {"status": "error", "content": [{"text": f"Error: {e!s}"}]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devduck
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: 🦆 Extreme minimalist self-adapting AI agent - one file, self-healing, runtime dependencies
|
|
5
5
|
Author-email: duck <hey@devduck.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -51,10 +51,22 @@ Minimalist AI that adapts to your environment and fixes itself when things break
|
|
|
51
51
|
|
|
52
52
|
## Install
|
|
53
53
|
|
|
54
|
+
**Homebrew (macOS/Linux):**
|
|
55
|
+
```bash
|
|
56
|
+
brew tap cagataycali/devduck
|
|
57
|
+
brew install devduck
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**pipx (all platforms):**
|
|
54
61
|
```bash
|
|
55
62
|
pipx install "devduck[all]" # Full install (recommended)
|
|
56
63
|
```
|
|
57
64
|
|
|
65
|
+
**uvx (instant run, no install):**
|
|
66
|
+
```bash
|
|
67
|
+
uvx devduck "hello world"
|
|
68
|
+
```
|
|
69
|
+
|
|
58
70
|
Requires: Python 3.10+, Ollama (or set `MODEL_PROVIDER`)
|
|
59
71
|
|
|
60
72
|
## Quick Start
|
|
@@ -246,11 +258,22 @@ Log location: `/tmp/devduck/logs/devduck.log`
|
|
|
246
258
|
Run DevDuck in CI/CD:
|
|
247
259
|
|
|
248
260
|
```yaml
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
261
|
+
name: AI Assistant
|
|
262
|
+
on: [issues, pull_request]
|
|
263
|
+
|
|
264
|
+
jobs:
|
|
265
|
+
assistant:
|
|
266
|
+
runs-on: ubuntu-latest
|
|
267
|
+
permissions:
|
|
268
|
+
contents: read
|
|
269
|
+
issues: write
|
|
270
|
+
pull-requests: write
|
|
271
|
+
steps:
|
|
272
|
+
- uses: cagataycali/devduck@main
|
|
273
|
+
with:
|
|
274
|
+
task: "Help with this issue or PR"
|
|
275
|
+
provider: "github"
|
|
276
|
+
model: "openai/o4-mini"
|
|
254
277
|
```
|
|
255
278
|
|
|
256
279
|
---
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
devduck/__init__.py,sha256=
|
|
1
|
+
devduck/__init__.py,sha256=Sdb1jdyy92bxYaP3kT0UN-8AdejWYoT2IrgiRiCR7XE,55852
|
|
2
2
|
devduck/__main__.py,sha256=aeF2RR4k7lzSR2X1QKV9XQPCKhtsH0JYUv2etBBqmL0,145
|
|
3
|
-
devduck/_version.py,sha256=
|
|
3
|
+
devduck/_version.py,sha256=k7cu0JKra64gmMNU_UfA5sw2eNc_GRvf3QmesiYAy8g,704
|
|
4
4
|
devduck/test_redduck.py,sha256=nqRchR7d54jWGx7JN5tji2ZV4Ek4L9s-P7hp0mKjA0Y,1773
|
|
5
5
|
devduck/tools/__init__.py,sha256=mu3V4jL2ACN4f-pnUID_A2p6o3Yc_-V_y9071PduCR0,177
|
|
6
6
|
devduck/tools/create_subagent.py,sha256=UzRz9BmU4PbTveZROEpZ311aH-u-i6x89gttu-CniAE,24687
|
|
7
7
|
devduck/tools/install_tools.py,sha256=wm_67b9IfY-2wRuWgxuEKhaSIV5vNfbGmZL3G9dGi2A,10348
|
|
8
8
|
devduck/tools/mcp_server.py,sha256=Ybp0PcJKW2TOvghsRL-i8Guqc9WokPwOD2bhVgzoj6Q,21490
|
|
9
9
|
devduck/tools/store_in_kb.py,sha256=-JM-oRQKR3FBubKHFHmXRnZSvi9dVgHxG0lismMgG2k,6861
|
|
10
|
+
devduck/tools/system_prompt.py,sha256=waAdmvRhyulorw_tLqpqUJN_AahuaeF2rXqjMqN7IRY,16905
|
|
10
11
|
devduck/tools/tcp.py,sha256=HkJ_j1t7hsPMxNL51bYHvPkHoTfro9Nov6vClwvwkEk,21943
|
|
11
12
|
devduck/tools/use_github.py,sha256=nr3JSGk48mKUobpgW__2gu6lFyUj93a1XRs3I6vH8W4,13682
|
|
12
13
|
devduck/tools/websocket.py,sha256=lRJZt813iHorVr5UI66Lq-lmaFuLYAfpodeV2gtda7k,16635
|
|
13
|
-
devduck-0.4.
|
|
14
|
-
devduck-0.4.
|
|
15
|
-
devduck-0.4.
|
|
16
|
-
devduck-0.4.
|
|
17
|
-
devduck-0.4.
|
|
18
|
-
devduck-0.4.
|
|
14
|
+
devduck-0.4.1.dist-info/licenses/LICENSE,sha256=CVGEiNh6cW1mgAKW83Q0P4xrQEXvqc6W-rb789W_IHM,1060
|
|
15
|
+
devduck-0.4.1.dist-info/METADATA,sha256=2rphJUQewjp06O_msTc9x7XKCtGURagV-STJ4jBBNHc,7747
|
|
16
|
+
devduck-0.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
+
devduck-0.4.1.dist-info/entry_points.txt,sha256=BAMQaIg_BLZQOTk12bT7hy1dE9oGPLt-_dTbI4cnBnQ,40
|
|
18
|
+
devduck-0.4.1.dist-info/top_level.txt,sha256=ySXWlVronp8xHYfQ_Hdfr463e0EnbWuqyuxs94EU7yk,8
|
|
19
|
+
devduck-0.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|