devduck 0.2.0__py3-none-any.whl → 0.4.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 +325 -99
- devduck/_version.py +2 -2
- devduck/tools/create_subagent.py +659 -0
- devduck/tools/mcp_server.py +34 -6
- devduck/tools/store_in_kb.py +187 -0
- devduck/tools/tcp.py +0 -3
- devduck/tools/use_github.py +438 -0
- devduck/tools/websocket.py +1 -1
- devduck-0.4.0.dist-info/METADATA +260 -0
- devduck-0.4.0.dist-info/RECORD +18 -0
- devduck/install.sh +0 -42
- devduck-0.2.0.dist-info/METADATA +0 -143
- devduck-0.2.0.dist-info/RECORD +0 -16
- {devduck-0.2.0.dist-info → devduck-0.4.0.dist-info}/WHEEL +0 -0
- {devduck-0.2.0.dist-info → devduck-0.4.0.dist-info}/entry_points.txt +0 -0
- {devduck-0.2.0.dist-info → devduck-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {devduck-0.2.0.dist-info → devduck-0.4.0.dist-info}/top_level.txt +0 -0
devduck/__init__.py
CHANGED
|
@@ -10,6 +10,7 @@ import platform
|
|
|
10
10
|
import socket
|
|
11
11
|
import logging
|
|
12
12
|
import tempfile
|
|
13
|
+
from datetime import datetime
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from datetime import datetime
|
|
15
16
|
from typing import Dict, Any
|
|
@@ -51,19 +52,18 @@ logger.info("DevDuck logging system initialized")
|
|
|
51
52
|
|
|
52
53
|
# 🔧 Self-healing dependency installer
|
|
53
54
|
def ensure_deps():
|
|
54
|
-
"""Install dependencies at runtime if missing"""
|
|
55
|
+
"""Install core dependencies at runtime if missing"""
|
|
55
56
|
import importlib.metadata
|
|
56
57
|
|
|
57
|
-
deps
|
|
58
|
+
# Only ensure core deps - everything else is optional
|
|
59
|
+
core_deps = [
|
|
58
60
|
"strands-agents",
|
|
59
|
-
"
|
|
60
|
-
"strands-agents[openai]",
|
|
61
|
-
"strands-agents[anthropic]",
|
|
61
|
+
"prompt_toolkit",
|
|
62
62
|
"strands-agents-tools",
|
|
63
63
|
]
|
|
64
64
|
|
|
65
65
|
# Check each package individually using importlib.metadata
|
|
66
|
-
for dep in
|
|
66
|
+
for dep in core_deps:
|
|
67
67
|
pkg_name = dep.split("[")[0] # Get base package name (strip extras)
|
|
68
68
|
try:
|
|
69
69
|
# Check if package is installed using metadata (checks PyPI package name)
|
|
@@ -606,7 +606,16 @@ def append_to_shell_history(query, response):
|
|
|
606
606
|
|
|
607
607
|
# 🦆 The devduck agent
|
|
608
608
|
class DevDuck:
|
|
609
|
-
def __init__(
|
|
609
|
+
def __init__(
|
|
610
|
+
self,
|
|
611
|
+
auto_start_servers=True,
|
|
612
|
+
tcp_port=9999,
|
|
613
|
+
ws_port=8080,
|
|
614
|
+
mcp_port=8000,
|
|
615
|
+
enable_tcp=True,
|
|
616
|
+
enable_ws=True,
|
|
617
|
+
enable_mcp=True,
|
|
618
|
+
):
|
|
610
619
|
"""Initialize the minimalist adaptive agent"""
|
|
611
620
|
logger.info("Initializing DevDuck agent...")
|
|
612
621
|
try:
|
|
@@ -622,27 +631,105 @@ class DevDuck:
|
|
|
622
631
|
|
|
623
632
|
# Import after ensuring deps
|
|
624
633
|
from strands import Agent, tool
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
634
|
+
|
|
635
|
+
# Core tools (always available)
|
|
636
|
+
core_tools = []
|
|
637
|
+
|
|
638
|
+
# Try importing optional tools gracefully
|
|
639
|
+
try:
|
|
640
|
+
from strands.models.ollama import OllamaModel
|
|
641
|
+
except ImportError:
|
|
642
|
+
logger.warning(
|
|
643
|
+
"strands-agents[ollama] not installed - Ollama model unavailable"
|
|
644
|
+
)
|
|
645
|
+
OllamaModel = None
|
|
646
|
+
|
|
647
|
+
try:
|
|
648
|
+
from strands_tools.utils.models.model import create_model
|
|
649
|
+
except ImportError:
|
|
650
|
+
logger.warning(
|
|
651
|
+
"strands-agents-tools not installed - create_model unavailable"
|
|
652
|
+
)
|
|
653
|
+
create_model = None
|
|
654
|
+
|
|
655
|
+
try:
|
|
656
|
+
from .tools import (
|
|
657
|
+
tcp,
|
|
658
|
+
websocket,
|
|
659
|
+
mcp_server,
|
|
660
|
+
install_tools,
|
|
661
|
+
use_github,
|
|
662
|
+
create_subagent,
|
|
663
|
+
store_in_kb,
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
core_tools.extend(
|
|
667
|
+
[
|
|
668
|
+
tcp,
|
|
669
|
+
websocket,
|
|
670
|
+
mcp_server,
|
|
671
|
+
install_tools,
|
|
672
|
+
use_github,
|
|
673
|
+
create_subagent,
|
|
674
|
+
store_in_kb,
|
|
675
|
+
]
|
|
676
|
+
)
|
|
677
|
+
except ImportError as e:
|
|
678
|
+
logger.warning(f"devduck.tools import failed: {e}")
|
|
679
|
+
|
|
680
|
+
# Skip fun tools in --mcp mode (they're not needed for MCP server)
|
|
681
|
+
if "--mcp" not in sys.argv:
|
|
682
|
+
try:
|
|
683
|
+
from strands_fun_tools import (
|
|
684
|
+
listen,
|
|
685
|
+
cursor,
|
|
686
|
+
clipboard,
|
|
687
|
+
screen_reader,
|
|
688
|
+
yolo_vision,
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
core_tools.extend(
|
|
692
|
+
[listen, cursor, clipboard, screen_reader, yolo_vision]
|
|
693
|
+
)
|
|
694
|
+
except ImportError:
|
|
695
|
+
logger.info(
|
|
696
|
+
"strands-fun-tools not installed - vision/audio tools unavailable (install with: pip install devduck[all])"
|
|
697
|
+
)
|
|
698
|
+
else:
|
|
699
|
+
logger.info("--mcp mode: skipping vision/audio tools")
|
|
700
|
+
|
|
701
|
+
try:
|
|
702
|
+
from strands_tools import (
|
|
703
|
+
shell,
|
|
704
|
+
editor,
|
|
705
|
+
calculator,
|
|
706
|
+
# python_repl,
|
|
707
|
+
image_reader,
|
|
708
|
+
use_agent,
|
|
709
|
+
load_tool,
|
|
710
|
+
environment,
|
|
711
|
+
mcp_client,
|
|
712
|
+
retrieve,
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
core_tools.extend(
|
|
716
|
+
[
|
|
717
|
+
shell,
|
|
718
|
+
editor,
|
|
719
|
+
calculator,
|
|
720
|
+
# python_repl,
|
|
721
|
+
image_reader,
|
|
722
|
+
use_agent,
|
|
723
|
+
load_tool,
|
|
724
|
+
environment,
|
|
725
|
+
mcp_client,
|
|
726
|
+
retrieve,
|
|
727
|
+
]
|
|
728
|
+
)
|
|
729
|
+
except ImportError:
|
|
730
|
+
logger.info(
|
|
731
|
+
"strands-agents-tools not installed - core tools unavailable (install with: pip install devduck[all])"
|
|
732
|
+
)
|
|
646
733
|
|
|
647
734
|
# Wrap system_prompt_tool with @tool decorator
|
|
648
735
|
@tool
|
|
@@ -665,39 +752,21 @@ class DevDuck:
|
|
|
665
752
|
"""View and manage DevDuck logs."""
|
|
666
753
|
return view_logs_tool(action, lines, pattern)
|
|
667
754
|
|
|
668
|
-
#
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
python_repl,
|
|
674
|
-
image_reader,
|
|
675
|
-
use_agent,
|
|
676
|
-
load_tool,
|
|
677
|
-
environment,
|
|
678
|
-
system_prompt,
|
|
679
|
-
view_logs,
|
|
680
|
-
tcp,
|
|
681
|
-
websocket,
|
|
682
|
-
mcp_server,
|
|
683
|
-
install_tools,
|
|
684
|
-
mcp_client,
|
|
685
|
-
listen,
|
|
686
|
-
cursor,
|
|
687
|
-
clipboard,
|
|
688
|
-
screen_reader,
|
|
689
|
-
yolo_vision,
|
|
690
|
-
]
|
|
755
|
+
# Add built-in tools to the toolset
|
|
756
|
+
core_tools.extend([system_prompt, view_logs])
|
|
757
|
+
|
|
758
|
+
# Assign tools
|
|
759
|
+
self.tools = core_tools
|
|
691
760
|
|
|
692
761
|
logger.info(f"Initialized {len(self.tools)} tools")
|
|
693
762
|
|
|
694
763
|
# Check if MODEL_PROVIDER env variable is set
|
|
695
764
|
model_provider = os.getenv("MODEL_PROVIDER")
|
|
696
765
|
|
|
697
|
-
if model_provider:
|
|
766
|
+
if model_provider and create_model:
|
|
698
767
|
# Use create_model utility for any provider (bedrock, anthropic, etc.)
|
|
699
768
|
self.agent_model = create_model(provider=model_provider)
|
|
700
|
-
|
|
769
|
+
elif OllamaModel:
|
|
701
770
|
# Fallback to default Ollama behavior
|
|
702
771
|
self.agent_model = OllamaModel(
|
|
703
772
|
host=self.ollama_host,
|
|
@@ -705,6 +774,10 @@ class DevDuck:
|
|
|
705
774
|
temperature=1,
|
|
706
775
|
keep_alive="5m",
|
|
707
776
|
)
|
|
777
|
+
else:
|
|
778
|
+
raise ImportError(
|
|
779
|
+
"No model provider available. Install with: pip install devduck[all]"
|
|
780
|
+
)
|
|
708
781
|
|
|
709
782
|
# Create agent with self-healing
|
|
710
783
|
self.agent = Agent(
|
|
@@ -714,54 +787,59 @@ class DevDuck:
|
|
|
714
787
|
load_tools_from_directory=True,
|
|
715
788
|
)
|
|
716
789
|
|
|
717
|
-
# 🚀 AUTO-START SERVERS: TCP
|
|
790
|
+
# 🚀 AUTO-START SERVERS: TCP, WebSocket, MCP HTTP
|
|
718
791
|
if auto_start_servers:
|
|
719
792
|
logger.info("Auto-starting servers...")
|
|
720
793
|
print("🦆 Auto-starting servers...")
|
|
721
794
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
795
|
+
if enable_tcp:
|
|
796
|
+
try:
|
|
797
|
+
# Start TCP server on configurable port
|
|
798
|
+
tcp_result = self.agent.tool.tcp(
|
|
799
|
+
action="start_server", port=tcp_port
|
|
800
|
+
)
|
|
801
|
+
if tcp_result.get("status") == "success":
|
|
802
|
+
logger.info(f"✓ TCP server started on port {tcp_port}")
|
|
803
|
+
print(f"🦆 ✓ TCP server: localhost:{tcp_port}")
|
|
804
|
+
else:
|
|
805
|
+
logger.warning(f"TCP server start issue: {tcp_result}")
|
|
806
|
+
except Exception as e:
|
|
807
|
+
logger.error(f"Failed to start TCP server: {e}")
|
|
808
|
+
print(f"🦆 ⚠ TCP server failed: {e}")
|
|
733
809
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
810
|
+
if enable_ws:
|
|
811
|
+
try:
|
|
812
|
+
# Start WebSocket server on configurable port
|
|
813
|
+
ws_result = self.agent.tool.websocket(
|
|
814
|
+
action="start_server", port=ws_port
|
|
815
|
+
)
|
|
816
|
+
if ws_result.get("status") == "success":
|
|
817
|
+
logger.info(f"✓ WebSocket server started on port {ws_port}")
|
|
818
|
+
print(f"🦆 ✓ WebSocket server: localhost:{ws_port}")
|
|
819
|
+
else:
|
|
820
|
+
logger.warning(f"WebSocket server start issue: {ws_result}")
|
|
821
|
+
except Exception as e:
|
|
822
|
+
logger.error(f"Failed to start WebSocket server: {e}")
|
|
823
|
+
print(f"🦆 ⚠ WebSocket server failed: {e}")
|
|
747
824
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
825
|
+
if enable_mcp:
|
|
826
|
+
try:
|
|
827
|
+
# Start MCP server with HTTP transport on configurable port
|
|
828
|
+
mcp_result = self.agent.tool.mcp_server(
|
|
829
|
+
action="start",
|
|
830
|
+
transport="http",
|
|
831
|
+
port=mcp_port,
|
|
832
|
+
expose_agent=True,
|
|
833
|
+
agent=self.agent,
|
|
834
|
+
)
|
|
835
|
+
if mcp_result.get("status") == "success":
|
|
836
|
+
logger.info(f"✓ MCP HTTP server started on port {mcp_port}")
|
|
837
|
+
print(f"🦆 ✓ MCP server: http://localhost:{mcp_port}/mcp")
|
|
838
|
+
else:
|
|
839
|
+
logger.warning(f"MCP server start issue: {mcp_result}")
|
|
840
|
+
except Exception as e:
|
|
841
|
+
logger.error(f"Failed to start MCP server: {e}")
|
|
842
|
+
print(f"🦆 ⚠ MCP server failed: {e}")
|
|
765
843
|
|
|
766
844
|
# Start file watcher for auto hot-reload
|
|
767
845
|
self._start_file_watcher()
|
|
@@ -847,6 +925,12 @@ You have full access to your own source code for self-awareness and self-modific
|
|
|
847
925
|
- Connect from Claude Desktop, other agents, or custom clients
|
|
848
926
|
- Full bidirectional communication
|
|
849
927
|
|
|
928
|
+
## Knowledge Base Integration:
|
|
929
|
+
- **Automatic RAG** - Set STRANDS_KNOWLEDGE_BASE_ID to enable automatic retrieval/storage
|
|
930
|
+
- Before each query: Retrieves relevant context from knowledge base
|
|
931
|
+
- After each response: Stores conversation for future reference
|
|
932
|
+
- Seamless memory across sessions without manual tool calls
|
|
933
|
+
|
|
850
934
|
## Tool Creation Patterns:
|
|
851
935
|
|
|
852
936
|
### **1. @tool Decorator:**
|
|
@@ -996,7 +1080,7 @@ def weather(action: str, location: str = None) -> Dict[str, Any]:
|
|
|
996
1080
|
self.agent = None
|
|
997
1081
|
|
|
998
1082
|
def __call__(self, query):
|
|
999
|
-
"""Make the agent callable"""
|
|
1083
|
+
"""Make the agent callable with automatic knowledge base integration"""
|
|
1000
1084
|
if not self.agent:
|
|
1001
1085
|
logger.warning("Agent unavailable - attempted to call with query")
|
|
1002
1086
|
return "🦆 Agent unavailable - try: devduck.restart()"
|
|
@@ -1006,8 +1090,37 @@ def weather(action: str, location: str = None) -> Dict[str, Any]:
|
|
|
1006
1090
|
# Mark agent as executing to prevent hot-reload interruption
|
|
1007
1091
|
self._agent_executing = True
|
|
1008
1092
|
|
|
1093
|
+
# 📚 Knowledge Base Retrieval (BEFORE agent runs)
|
|
1094
|
+
knowledge_base_id = os.getenv("STRANDS_KNOWLEDGE_BASE_ID")
|
|
1095
|
+
if knowledge_base_id and hasattr(self.agent, "tool"):
|
|
1096
|
+
try:
|
|
1097
|
+
if "retrieve" in self.agent.tool_names:
|
|
1098
|
+
logger.info(f"Retrieving context from KB: {knowledge_base_id}")
|
|
1099
|
+
self.agent.tool.retrieve(
|
|
1100
|
+
text=query, knowledgeBaseId=knowledge_base_id
|
|
1101
|
+
)
|
|
1102
|
+
except Exception as e:
|
|
1103
|
+
logger.warning(f"KB retrieval failed: {e}")
|
|
1104
|
+
|
|
1105
|
+
# Run the agent
|
|
1009
1106
|
result = self.agent(query)
|
|
1010
1107
|
|
|
1108
|
+
# 💾 Knowledge Base Storage (AFTER agent runs)
|
|
1109
|
+
if knowledge_base_id and hasattr(self.agent, "tool"):
|
|
1110
|
+
try:
|
|
1111
|
+
if "store_in_kb" in self.agent.tool_names:
|
|
1112
|
+
|
|
1113
|
+
conversation_content = f"Input: {query}, Result: {result!s}"
|
|
1114
|
+
conversation_title = f"DevDuck: {datetime.now().strftime('%Y-%m-%d')} | {query[:500]}"
|
|
1115
|
+
self.agent.tool.store_in_kb(
|
|
1116
|
+
content=conversation_content,
|
|
1117
|
+
title=conversation_title,
|
|
1118
|
+
knowledge_base_id=knowledge_base_id,
|
|
1119
|
+
)
|
|
1120
|
+
logger.info(f"Stored conversation in KB: {knowledge_base_id}")
|
|
1121
|
+
except Exception as e:
|
|
1122
|
+
logger.warning(f"KB storage failed: {e}")
|
|
1123
|
+
|
|
1011
1124
|
# Agent finished - check if reload was pending
|
|
1012
1125
|
self._agent_executing = False
|
|
1013
1126
|
logger.info("Agent call completed successfully")
|
|
@@ -1157,7 +1270,30 @@ def weather(action: str, location: str = None) -> Dict[str, Any]:
|
|
|
1157
1270
|
|
|
1158
1271
|
|
|
1159
1272
|
# 🦆 Auto-initialize when imported
|
|
1160
|
-
|
|
1273
|
+
# Check environment variables to control server configuration
|
|
1274
|
+
# Also check if --mcp flag is present to skip auto-starting servers
|
|
1275
|
+
_auto_start = os.getenv("DEVDUCK_AUTO_START_SERVERS", "true").lower() == "true"
|
|
1276
|
+
|
|
1277
|
+
# Disable auto-start if --mcp flag is present (stdio mode)
|
|
1278
|
+
if "--mcp" in sys.argv:
|
|
1279
|
+
_auto_start = False
|
|
1280
|
+
|
|
1281
|
+
_tcp_port = int(os.getenv("DEVDUCK_TCP_PORT", "9999"))
|
|
1282
|
+
_ws_port = int(os.getenv("DEVDUCK_WS_PORT", "8080"))
|
|
1283
|
+
_mcp_port = int(os.getenv("DEVDUCK_MCP_PORT", "8000"))
|
|
1284
|
+
_enable_tcp = os.getenv("DEVDUCK_ENABLE_TCP", "true").lower() == "true"
|
|
1285
|
+
_enable_ws = os.getenv("DEVDUCK_ENABLE_WS", "true").lower() == "true"
|
|
1286
|
+
_enable_mcp = os.getenv("DEVDUCK_ENABLE_MCP", "true").lower() == "true"
|
|
1287
|
+
|
|
1288
|
+
devduck = DevDuck(
|
|
1289
|
+
auto_start_servers=_auto_start,
|
|
1290
|
+
tcp_port=_tcp_port,
|
|
1291
|
+
ws_port=_ws_port,
|
|
1292
|
+
mcp_port=_mcp_port,
|
|
1293
|
+
enable_tcp=_enable_tcp,
|
|
1294
|
+
enable_ws=_enable_ws,
|
|
1295
|
+
enable_mcp=_enable_mcp,
|
|
1296
|
+
)
|
|
1161
1297
|
|
|
1162
1298
|
|
|
1163
1299
|
# 🚀 Convenience functions
|
|
@@ -1426,9 +1562,99 @@ You have full access to your own source code for self-awareness and self-modific
|
|
|
1426
1562
|
|
|
1427
1563
|
def cli():
|
|
1428
1564
|
"""CLI entry point for pip-installed devduck command"""
|
|
1565
|
+
import argparse
|
|
1566
|
+
|
|
1567
|
+
parser = argparse.ArgumentParser(
|
|
1568
|
+
description="🦆 DevDuck - Extreme minimalist self-adapting agent",
|
|
1569
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
1570
|
+
epilog="""
|
|
1571
|
+
Examples:
|
|
1572
|
+
devduck # Start interactive mode
|
|
1573
|
+
devduck "your query here" # One-shot query
|
|
1574
|
+
devduck --mcp # MCP stdio mode (for Claude Desktop)
|
|
1575
|
+
devduck --tcp-port 9000 # Custom TCP port
|
|
1576
|
+
devduck --no-tcp --no-ws # Disable TCP and WebSocket
|
|
1577
|
+
devduck --mcp-port 3000 # Custom MCP port
|
|
1578
|
+
|
|
1579
|
+
Claude Desktop Config:
|
|
1580
|
+
{
|
|
1581
|
+
"mcpServers": {
|
|
1582
|
+
"devduck": {
|
|
1583
|
+
"command": "uvx",
|
|
1584
|
+
"args": ["devduck", "--mcp"]
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
""",
|
|
1589
|
+
)
|
|
1590
|
+
|
|
1591
|
+
# Query argument
|
|
1592
|
+
parser.add_argument("query", nargs="*", help="Query to send to the agent")
|
|
1593
|
+
|
|
1594
|
+
# MCP stdio mode flag
|
|
1595
|
+
parser.add_argument(
|
|
1596
|
+
"--mcp",
|
|
1597
|
+
action="store_true",
|
|
1598
|
+
help="Start MCP server in stdio mode (for Claude Desktop integration)",
|
|
1599
|
+
)
|
|
1600
|
+
|
|
1601
|
+
# Server configuration
|
|
1602
|
+
parser.add_argument(
|
|
1603
|
+
"--tcp-port", type=int, default=9999, help="TCP server port (default: 9999)"
|
|
1604
|
+
)
|
|
1605
|
+
parser.add_argument(
|
|
1606
|
+
"--ws-port",
|
|
1607
|
+
type=int,
|
|
1608
|
+
default=8080,
|
|
1609
|
+
help="WebSocket server port (default: 8080)",
|
|
1610
|
+
)
|
|
1611
|
+
parser.add_argument(
|
|
1612
|
+
"--mcp-port",
|
|
1613
|
+
type=int,
|
|
1614
|
+
default=8000,
|
|
1615
|
+
help="MCP HTTP server port (default: 8000)",
|
|
1616
|
+
)
|
|
1617
|
+
|
|
1618
|
+
# Server enable/disable flags
|
|
1619
|
+
parser.add_argument("--no-tcp", action="store_true", help="Disable TCP server")
|
|
1620
|
+
parser.add_argument("--no-ws", action="store_true", help="Disable WebSocket server")
|
|
1621
|
+
parser.add_argument("--no-mcp", action="store_true", help="Disable MCP server")
|
|
1622
|
+
parser.add_argument(
|
|
1623
|
+
"--no-servers",
|
|
1624
|
+
action="store_true",
|
|
1625
|
+
help="Disable all servers (no TCP, WebSocket, or MCP)",
|
|
1626
|
+
)
|
|
1627
|
+
|
|
1628
|
+
args = parser.parse_args()
|
|
1629
|
+
|
|
1429
1630
|
logger.info("CLI mode started")
|
|
1430
|
-
|
|
1431
|
-
|
|
1631
|
+
|
|
1632
|
+
# Handle --mcp flag for stdio mode
|
|
1633
|
+
if args.mcp:
|
|
1634
|
+
logger.info("Starting MCP server in stdio mode (blocking, foreground)")
|
|
1635
|
+
print("🦆 Starting MCP stdio server...", file=sys.stderr)
|
|
1636
|
+
|
|
1637
|
+
# Don't auto-start HTTP/TCP/WS servers for stdio mode
|
|
1638
|
+
if devduck.agent:
|
|
1639
|
+
try:
|
|
1640
|
+
# Start MCP server in stdio mode - this BLOCKS until terminated
|
|
1641
|
+
devduck.agent.tool.mcp_server(
|
|
1642
|
+
action="start",
|
|
1643
|
+
transport="stdio",
|
|
1644
|
+
expose_agent=True,
|
|
1645
|
+
agent=devduck.agent,
|
|
1646
|
+
)
|
|
1647
|
+
except Exception as e:
|
|
1648
|
+
logger.error(f"Failed to start MCP stdio server: {e}")
|
|
1649
|
+
print(f"🦆 Error: {e}", file=sys.stderr)
|
|
1650
|
+
sys.exit(1)
|
|
1651
|
+
else:
|
|
1652
|
+
print("🦆 Agent not available", file=sys.stderr)
|
|
1653
|
+
sys.exit(1)
|
|
1654
|
+
return
|
|
1655
|
+
|
|
1656
|
+
if args.query:
|
|
1657
|
+
query = " ".join(args.query)
|
|
1432
1658
|
logger.info(f"CLI query: {query}")
|
|
1433
1659
|
result = ask(query)
|
|
1434
1660
|
print(result)
|
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.4.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 4, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|