devduck 0.4.0__py3-none-any.whl → 0.5.0__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 +64 -179
- devduck/_version.py +2 -2
- devduck/tools/__init__.py +3 -1
- devduck/tools/_ambient_input.py +423 -0
- devduck/tools/_tray_app.py +522 -0
- devduck/tools/ambient.py +157 -0
- devduck/tools/ipc.py +543 -0
- devduck/tools/system_prompt.py +485 -0
- devduck/tools/tcp.py +0 -4
- devduck/tools/tray.py +246 -0
- devduck-0.5.0.dist-info/METADATA +554 -0
- devduck-0.5.0.dist-info/RECORD +24 -0
- {devduck-0.4.0.dist-info → devduck-0.5.0.dist-info}/entry_points.txt +1 -0
- devduck-0.5.0.dist-info/licenses/LICENSE +201 -0
- devduck-0.4.0.dist-info/METADATA +0 -260
- devduck-0.4.0.dist-info/RECORD +0 -18
- devduck-0.4.0.dist-info/licenses/LICENSE +0 -21
- {devduck-0.4.0.dist-info → devduck-0.5.0.dist-info}/WHEEL +0 -0
- {devduck-0.4.0.dist-info → devduck-0.5.0.dist-info}/top_level.txt +0 -0
devduck/__init__.py
CHANGED
|
@@ -18,6 +18,7 @@ from logging.handlers import RotatingFileHandler
|
|
|
18
18
|
|
|
19
19
|
os.environ["BYPASS_TOOL_CONSENT"] = "true"
|
|
20
20
|
os.environ["STRANDS_TOOL_CONSOLE_MODE"] = "enabled"
|
|
21
|
+
os.environ["EDITOR_DISABLE_BACKUP"] = "true"
|
|
21
22
|
|
|
22
23
|
# 📝 Setup logging system
|
|
23
24
|
LOG_DIR = Path(tempfile.gettempdir()) / "devduck" / "logs"
|
|
@@ -127,167 +128,6 @@ def get_own_source_code():
|
|
|
127
128
|
return f"Error reading own source code: {e}"
|
|
128
129
|
|
|
129
130
|
|
|
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
131
|
def view_logs_tool(
|
|
292
132
|
action: str = "view",
|
|
293
133
|
lines: int = 100,
|
|
@@ -612,9 +452,11 @@ class DevDuck:
|
|
|
612
452
|
tcp_port=9999,
|
|
613
453
|
ws_port=8080,
|
|
614
454
|
mcp_port=8000,
|
|
455
|
+
ipc_socket=None,
|
|
615
456
|
enable_tcp=True,
|
|
616
457
|
enable_ws=True,
|
|
617
458
|
enable_mcp=True,
|
|
459
|
+
enable_ipc=True,
|
|
618
460
|
):
|
|
619
461
|
"""Initialize the minimalist adaptive agent"""
|
|
620
462
|
logger.info("Initializing DevDuck agent...")
|
|
@@ -656,22 +498,30 @@ class DevDuck:
|
|
|
656
498
|
from .tools import (
|
|
657
499
|
tcp,
|
|
658
500
|
websocket,
|
|
501
|
+
ipc,
|
|
659
502
|
mcp_server,
|
|
660
503
|
install_tools,
|
|
661
504
|
use_github,
|
|
662
505
|
create_subagent,
|
|
663
506
|
store_in_kb,
|
|
507
|
+
system_prompt,
|
|
508
|
+
tray,
|
|
509
|
+
ambient,
|
|
664
510
|
)
|
|
665
511
|
|
|
666
512
|
core_tools.extend(
|
|
667
513
|
[
|
|
668
514
|
tcp,
|
|
669
515
|
websocket,
|
|
516
|
+
ipc,
|
|
670
517
|
mcp_server,
|
|
671
518
|
install_tools,
|
|
672
519
|
use_github,
|
|
673
520
|
create_subagent,
|
|
674
521
|
store_in_kb,
|
|
522
|
+
system_prompt,
|
|
523
|
+
tray,
|
|
524
|
+
ambient,
|
|
675
525
|
]
|
|
676
526
|
)
|
|
677
527
|
except ImportError as e:
|
|
@@ -702,6 +552,8 @@ class DevDuck:
|
|
|
702
552
|
from strands_tools import (
|
|
703
553
|
shell,
|
|
704
554
|
editor,
|
|
555
|
+
file_read,
|
|
556
|
+
file_write,
|
|
705
557
|
calculator,
|
|
706
558
|
# python_repl,
|
|
707
559
|
image_reader,
|
|
@@ -716,6 +568,8 @@ class DevDuck:
|
|
|
716
568
|
[
|
|
717
569
|
shell,
|
|
718
570
|
editor,
|
|
571
|
+
file_read,
|
|
572
|
+
file_write,
|
|
719
573
|
calculator,
|
|
720
574
|
# python_repl,
|
|
721
575
|
image_reader,
|
|
@@ -731,17 +585,6 @@ class DevDuck:
|
|
|
731
585
|
"strands-agents-tools not installed - core tools unavailable (install with: pip install devduck[all])"
|
|
732
586
|
)
|
|
733
587
|
|
|
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
588
|
# Wrap view_logs_tool with @tool decorator
|
|
746
589
|
@tool
|
|
747
590
|
def view_logs(
|
|
@@ -753,7 +596,7 @@ class DevDuck:
|
|
|
753
596
|
return view_logs_tool(action, lines, pattern)
|
|
754
597
|
|
|
755
598
|
# Add built-in tools to the toolset
|
|
756
|
-
core_tools.extend([
|
|
599
|
+
core_tools.extend([view_logs])
|
|
757
600
|
|
|
758
601
|
# Assign tools
|
|
759
602
|
self.tools = core_tools
|
|
@@ -841,6 +684,22 @@ class DevDuck:
|
|
|
841
684
|
logger.error(f"Failed to start MCP server: {e}")
|
|
842
685
|
print(f"🦆 ⚠ MCP server failed: {e}")
|
|
843
686
|
|
|
687
|
+
if enable_ipc:
|
|
688
|
+
try:
|
|
689
|
+
# Start IPC server for local process communication
|
|
690
|
+
ipc_socket_path = ipc_socket or "/tmp/devduck_main.sock"
|
|
691
|
+
ipc_result = self.agent.tool.ipc(
|
|
692
|
+
action="start_server", socket_path=ipc_socket_path
|
|
693
|
+
)
|
|
694
|
+
if ipc_result.get("status") == "success":
|
|
695
|
+
logger.info(f"✓ IPC server started on {ipc_socket_path}")
|
|
696
|
+
print(f"🦆 ✓ IPC server: {ipc_socket_path}")
|
|
697
|
+
else:
|
|
698
|
+
logger.warning(f"IPC server start issue: {ipc_result}")
|
|
699
|
+
except Exception as e:
|
|
700
|
+
logger.error(f"Failed to start IPC server: {e}")
|
|
701
|
+
print(f"🦆 ⚠ IPC server failed: {e}")
|
|
702
|
+
|
|
844
703
|
# Start file watcher for auto hot-reload
|
|
845
704
|
self._start_file_watcher()
|
|
846
705
|
|
|
@@ -980,9 +839,20 @@ def weather(action: str, location: str = None) -> Dict[str, Any]:
|
|
|
980
839
|
```
|
|
981
840
|
|
|
982
841
|
## System Prompt Management:
|
|
983
|
-
-
|
|
984
|
-
-
|
|
985
|
-
-
|
|
842
|
+
- **View**: system_prompt(action='view') - See current prompt
|
|
843
|
+
- **Update Local**: system_prompt(action='update', prompt='new text') - Updates env var + .prompt file
|
|
844
|
+
- **Update GitHub**: system_prompt(action='update', prompt='text', repository='cagataycali/devduck') - Syncs to repo variables
|
|
845
|
+
- **Variable Name**: system_prompt(action='update', prompt='text', variable_name='CUSTOM_PROMPT') - Use custom var
|
|
846
|
+
- **Add Context**: system_prompt(action='add_context', context='new learning') - Append without replacing
|
|
847
|
+
|
|
848
|
+
### 🧠 Self-Improvement Pattern:
|
|
849
|
+
When you learn something valuable during conversations:
|
|
850
|
+
1. Identify the new insight or pattern
|
|
851
|
+
2. Use system_prompt(action='add_context', context='...') to append it
|
|
852
|
+
3. Sync to GitHub: system_prompt(action='update', prompt=new_full_prompt, repository='owner/repo')
|
|
853
|
+
4. New learnings persist across sessions via SYSTEM_PROMPT env var
|
|
854
|
+
|
|
855
|
+
**Repository Integration**: Set repository='cagataycali/devduck' to sync prompts across deployments
|
|
986
856
|
|
|
987
857
|
## Shell Commands:
|
|
988
858
|
- Prefix with ! to execute shell commands directly
|
|
@@ -1281,18 +1151,22 @@ if "--mcp" in sys.argv:
|
|
|
1281
1151
|
_tcp_port = int(os.getenv("DEVDUCK_TCP_PORT", "9999"))
|
|
1282
1152
|
_ws_port = int(os.getenv("DEVDUCK_WS_PORT", "8080"))
|
|
1283
1153
|
_mcp_port = int(os.getenv("DEVDUCK_MCP_PORT", "8000"))
|
|
1154
|
+
_ipc_socket = os.getenv("DEVDUCK_IPC_SOCKET", None)
|
|
1284
1155
|
_enable_tcp = os.getenv("DEVDUCK_ENABLE_TCP", "true").lower() == "true"
|
|
1285
1156
|
_enable_ws = os.getenv("DEVDUCK_ENABLE_WS", "true").lower() == "true"
|
|
1286
1157
|
_enable_mcp = os.getenv("DEVDUCK_ENABLE_MCP", "true").lower() == "true"
|
|
1158
|
+
_enable_ipc = os.getenv("DEVDUCK_ENABLE_IPC", "true").lower() == "true"
|
|
1287
1159
|
|
|
1288
1160
|
devduck = DevDuck(
|
|
1289
1161
|
auto_start_servers=_auto_start,
|
|
1290
1162
|
tcp_port=_tcp_port,
|
|
1291
1163
|
ws_port=_ws_port,
|
|
1292
1164
|
mcp_port=_mcp_port,
|
|
1165
|
+
ipc_socket=_ipc_socket,
|
|
1293
1166
|
enable_tcp=_enable_tcp,
|
|
1294
1167
|
enable_ws=_enable_ws,
|
|
1295
1168
|
enable_mcp=_enable_mcp,
|
|
1169
|
+
enable_ipc=_enable_ipc,
|
|
1296
1170
|
)
|
|
1297
1171
|
|
|
1298
1172
|
|
|
@@ -1520,9 +1394,20 @@ You have full access to your own source code for self-awareness and self-modific
|
|
|
1520
1394
|
- Full bidirectional communication
|
|
1521
1395
|
|
|
1522
1396
|
## System Prompt Management:
|
|
1523
|
-
-
|
|
1524
|
-
-
|
|
1525
|
-
-
|
|
1397
|
+
- **View**: system_prompt(action='view') - See current prompt
|
|
1398
|
+
- **Update Local**: system_prompt(action='update', prompt='new text') - Updates env var + .prompt file
|
|
1399
|
+
- **Update GitHub**: system_prompt(action='update', prompt='text', repository='cagataycali/devduck') - Syncs to repo variables
|
|
1400
|
+
- **Variable Name**: system_prompt(action='update', prompt='text', variable_name='CUSTOM_PROMPT') - Use custom var
|
|
1401
|
+
- **Add Context**: system_prompt(action='add_context', context='new learning') - Append without replacing
|
|
1402
|
+
|
|
1403
|
+
### 🧠 Self-Improvement Pattern:
|
|
1404
|
+
When you learn something valuable during conversations:
|
|
1405
|
+
1. Identify the new insight or pattern
|
|
1406
|
+
2. Use system_prompt(action='add_context', context='...') to append it
|
|
1407
|
+
3. Optionally sync to GitHub: system_prompt(action='update', prompt=new_full_prompt, repository='owner/repo')
|
|
1408
|
+
4. New learnings persist across sessions via SYSTEM_PROMPT env var
|
|
1409
|
+
|
|
1410
|
+
**Repository Integration**: Set repository='cagataycali/devduck' to sync prompts across deployments
|
|
1526
1411
|
|
|
1527
1412
|
## Shell Commands:
|
|
1528
1413
|
- 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.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.5.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 5, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
devduck/tools/__init__.py
CHANGED
|
@@ -3,5 +3,7 @@
|
|
|
3
3
|
from .tcp import tcp
|
|
4
4
|
from .mcp_server import mcp_server
|
|
5
5
|
from .install_tools import install_tools
|
|
6
|
+
from .tray import tray
|
|
7
|
+
from .ambient import ambient
|
|
6
8
|
|
|
7
|
-
__all__ = ["tcp", "mcp_server", "install_tools"]
|
|
9
|
+
__all__ = ["tcp", "mcp_server", "install_tools", "tray", "ambient"]
|