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 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([system_prompt, view_logs])
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
- - Use system_prompt(action='get') to view current prompt
984
- - Use system_prompt(action='set', prompt='new text') to update
985
- - Changes persist in SYSTEM_PROMPT environment variable
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
- - Use system_prompt(action='get') to view current prompt
1524
- - Use system_prompt(action='set', prompt='new text') to update
1525
- - Changes persist in SYSTEM_PROMPT environment variable
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.4.0'
32
- __version_tuple__ = version_tuple = (0, 4, 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"]