devduck 0.2.0__tar.gz → 0.3.0__tar.gz
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-0.3.0/.github/workflows/agent.yml +163 -0
- {devduck-0.2.0 → devduck-0.3.0}/.gitignore +3 -1
- {devduck-0.2.0/devduck.egg-info → devduck-0.3.0}/PKG-INFO +17 -8
- {devduck-0.2.0 → devduck-0.3.0}/README.md +5 -1
- devduck-0.3.0/action.yml +212 -0
- devduck-0.3.0/agent_runner.py +206 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck/__init__.py +273 -99
- {devduck-0.2.0 → devduck-0.3.0}/devduck/_version.py +3 -3
- devduck-0.3.0/devduck/tools/create_subagent.py +659 -0
- devduck-0.3.0/devduck/tools/store_in_kb.py +187 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck/tools/tcp.py +0 -3
- devduck-0.3.0/devduck/tools/use_github.py +438 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck/tools/websocket.py +1 -1
- {devduck-0.2.0 → devduck-0.3.0/devduck.egg-info}/PKG-INFO +17 -8
- {devduck-0.2.0 → devduck-0.3.0}/devduck.egg-info/SOURCES.txt +17 -2
- {devduck-0.2.0 → devduck-0.3.0}/devduck.egg-info/requires.txt +9 -3
- devduck-0.3.0/mcp.json +10 -0
- {devduck-0.2.0 → devduck-0.3.0}/pyproject.toml +11 -3
- devduck-0.3.0/requirements.txt +4 -0
- devduck-0.3.0/setup-aws-oidc.sh +145 -0
- devduck-0.3.0/tools/__init__.py +0 -0
- devduck-0.3.0/tools/fetch_github_tool.py +201 -0
- devduck-0.3.0/tools/gist.py +708 -0
- devduck-0.3.0/tools/github_tools.py +134 -0
- devduck-0.3.0/tools/scraper.py +935 -0
- devduck-0.3.0/tools/store_in_kb.py +187 -0
- devduck-0.3.0/tools/system_prompt.py +485 -0
- devduck-0.2.0/devduck/install.sh +0 -42
- {devduck-0.2.0 → devduck-0.3.0}/LICENSE +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/MANIFEST.in +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck/__main__.py +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck/test_redduck.py +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck/tools/__init__.py +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck/tools/install_tools.py +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck/tools/mcp_server.py +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck.egg-info/dependency_links.txt +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck.egg-info/entry_points.txt +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/devduck.egg-info/top_level.txt +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/docs/index.html +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/install.sh +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/setup.cfg +0 -0
- {devduck-0.2.0 → devduck-0.3.0}/test.py +0 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
name: DevDuck
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: '0 8 * * *'
|
|
6
|
+
issues:
|
|
7
|
+
types: [opened, edited, closed, reopened, assigned, unassigned, labeled, unlabeled]
|
|
8
|
+
issue_comment:
|
|
9
|
+
types: [created, edited, deleted]
|
|
10
|
+
pull_request:
|
|
11
|
+
types: [opened, closed, edited, reopened, synchronize, ready_for_review]
|
|
12
|
+
pull_request_review:
|
|
13
|
+
types: [submitted, edited]
|
|
14
|
+
discussion:
|
|
15
|
+
types: [created, edited, answered, unanswered, category_changed, labeled, unlabeled, transferred, pinned, unpinned, locked, unlocked]
|
|
16
|
+
discussion_comment:
|
|
17
|
+
types: [created, edited, deleted]
|
|
18
|
+
pull_request_review_comment:
|
|
19
|
+
types: [created, edited]
|
|
20
|
+
workflow_dispatch:
|
|
21
|
+
inputs:
|
|
22
|
+
task:
|
|
23
|
+
description: 'Task'
|
|
24
|
+
required: false
|
|
25
|
+
type: string
|
|
26
|
+
system_prompt:
|
|
27
|
+
description: 'System prompt'
|
|
28
|
+
required: false
|
|
29
|
+
type: string
|
|
30
|
+
tools:
|
|
31
|
+
description: 'Comma-separated list of tools to enable for the agent'
|
|
32
|
+
required: false
|
|
33
|
+
type: string
|
|
34
|
+
provider:
|
|
35
|
+
description: 'Provider'
|
|
36
|
+
default: "bedrock"
|
|
37
|
+
required: false
|
|
38
|
+
type: choice
|
|
39
|
+
options:
|
|
40
|
+
- bedrock
|
|
41
|
+
- openai
|
|
42
|
+
- github
|
|
43
|
+
- anthropic
|
|
44
|
+
- litellm
|
|
45
|
+
- llamaapi
|
|
46
|
+
model:
|
|
47
|
+
description: 'Model ID'
|
|
48
|
+
default: "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
|
49
|
+
required: false
|
|
50
|
+
type: string
|
|
51
|
+
max_tokens:
|
|
52
|
+
description: 'Max tokens'
|
|
53
|
+
default: "60000"
|
|
54
|
+
required: false
|
|
55
|
+
type: string
|
|
56
|
+
temperature:
|
|
57
|
+
description: 'Temperature (0.0-1.0)'
|
|
58
|
+
default: "1"
|
|
59
|
+
required: false
|
|
60
|
+
type: string
|
|
61
|
+
mcp_servers:
|
|
62
|
+
description: 'MCP servers configuration (JSON string)'
|
|
63
|
+
required: false
|
|
64
|
+
type: string
|
|
65
|
+
default: '{"mcpServers":{"strands-docs":{"command":"uvx","args":["strands-agents-mcp-server"],"timeout":30000}}}'
|
|
66
|
+
additional_request_fields:
|
|
67
|
+
description: 'Additional request fields JSON (e.g., thinking, anthropic_beta)'
|
|
68
|
+
required: false
|
|
69
|
+
type: string
|
|
70
|
+
|
|
71
|
+
permissions: write-all
|
|
72
|
+
|
|
73
|
+
jobs:
|
|
74
|
+
check-permissions:
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
outputs:
|
|
77
|
+
authorized: ${{ steps.check.outputs.authorized }}
|
|
78
|
+
steps:
|
|
79
|
+
- name: Check user authorization
|
|
80
|
+
id: check
|
|
81
|
+
run: |
|
|
82
|
+
# 🔐 CONFIGURE AUTHORIZED USERS HERE
|
|
83
|
+
# Add your authorized usernames (comma-separated)
|
|
84
|
+
AUTHORIZED_USERS="${{ secrets.AUTHORIZED_USERS }}"
|
|
85
|
+
|
|
86
|
+
echo "Checking authorization for user: ${{ github.actor }}"
|
|
87
|
+
|
|
88
|
+
# Check if the triggering user is in the authorized list
|
|
89
|
+
if [[ ",$AUTHORIZED_USERS," == *",${{ github.actor }},"* ]]; then
|
|
90
|
+
echo "✅ User ${{ github.actor }} is authorized"
|
|
91
|
+
echo "authorized=true" >> $GITHUB_OUTPUT
|
|
92
|
+
else
|
|
93
|
+
echo "❌ User ${{ github.actor }} is NOT authorized"
|
|
94
|
+
echo "Authorized users: $AUTHORIZED_USERS"
|
|
95
|
+
echo "authorized=false" >> $GITHUB_OUTPUT
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
agent:
|
|
99
|
+
needs: check-permissions
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
# Only run if user is authorized
|
|
102
|
+
if: needs.check-permissions.outputs.authorized == 'true'
|
|
103
|
+
steps:
|
|
104
|
+
- name: Checkout code
|
|
105
|
+
uses: actions/checkout@v4
|
|
106
|
+
with:
|
|
107
|
+
token: ${{ secrets.PAT_TOKEN }}
|
|
108
|
+
|
|
109
|
+
- name: Run Strands Agent
|
|
110
|
+
uses: ./
|
|
111
|
+
env:
|
|
112
|
+
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
|
|
113
|
+
MODEL_PROVIDER: "bedrock"
|
|
114
|
+
with:
|
|
115
|
+
task: |
|
|
116
|
+
${{
|
|
117
|
+
github.event.inputs.task ||
|
|
118
|
+
'I received a GitHub event and am running autonomously. I will analyze the context and take appropriate action. I have full GitHub event details available.'
|
|
119
|
+
}}
|
|
120
|
+
|
|
121
|
+
# Model configuration
|
|
122
|
+
provider: ${{ github.event.inputs.provider || vars.STRANDS_PROVIDER || 'bedrock' }}
|
|
123
|
+
model: ${{ github.event.inputs.model || vars.STRANDS_MODEL_ID || 'us.anthropic.claude-sonnet-4-5-20250929-v1:0' }}
|
|
124
|
+
max_tokens: ${{ github.event.inputs.max_tokens || vars.STRANDS_MAX_TOKENS || '60000' }}
|
|
125
|
+
temperature: ${{ github.event.inputs.temperature || vars.STRANDS_TEMPERATURE || '1' }}
|
|
126
|
+
|
|
127
|
+
# System prompt configuration
|
|
128
|
+
system_prompt: ${{ github.event.inputs.system_prompt || vars.SYSTEM_PROMPT || vars.INPUT_SYSTEM_PROMPT || 'You are a restricted GitHub agent for this repository, powered by Strands Agents SDK. Only authorized users can trigger your execution.' }}
|
|
129
|
+
|
|
130
|
+
# Tool configuration
|
|
131
|
+
tools: ${{ github.event.inputs.tools || vars.STRANDS_TOOLS}}
|
|
132
|
+
|
|
133
|
+
# AWS configuration
|
|
134
|
+
aws_role_arn: ${{ secrets.AWS_ROLE_ARN }}
|
|
135
|
+
aws_region: ${{ secrets.AWS_REGION || 'us-west-2' }}
|
|
136
|
+
|
|
137
|
+
# Knowledge base
|
|
138
|
+
knowledge_base_id: ${{ secrets.STRANDS_KNOWLEDGE_BASE_ID }}
|
|
139
|
+
|
|
140
|
+
# Advanced configuration
|
|
141
|
+
additional_request_fields: ${{ github.event.inputs.additional_request_fields || vars.STRANDS_ADDITIONAL_REQUEST_FIELDS }}
|
|
142
|
+
bypass_tool_consent: "true"
|
|
143
|
+
|
|
144
|
+
# MCP configuration
|
|
145
|
+
mcp_servers: ${{ github.event.inputs.mcp_servers || vars.MCP_SERVERS }}
|
|
146
|
+
|
|
147
|
+
unauthorized-notice:
|
|
148
|
+
needs: check-permissions
|
|
149
|
+
runs-on: ubuntu-latest
|
|
150
|
+
# Only run if user is NOT authorized
|
|
151
|
+
if: needs.check-permissions.outputs.authorized == 'false'
|
|
152
|
+
steps:
|
|
153
|
+
- name: Unauthorized access attempt
|
|
154
|
+
run: |
|
|
155
|
+
echo "🚫 UNAUTHORIZED ACCESS ATTEMPT"
|
|
156
|
+
echo "User: ${{ github.actor }}"
|
|
157
|
+
echo "Repository: ${{ github.repository }}"
|
|
158
|
+
echo "Event: ${{ github.event_name }}"
|
|
159
|
+
echo "Time: $(date)"
|
|
160
|
+
echo ""
|
|
161
|
+
echo "This incident has been logged."
|
|
162
|
+
echo "Contact repository administrators for access."
|
|
163
|
+
exit 1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devduck
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
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
|
|
@@ -27,15 +27,20 @@ Requires-Python: >=3.10
|
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
License-File: LICENSE
|
|
29
29
|
Requires-Dist: strands-agents
|
|
30
|
+
Requires-Dist: prompt_toolkit
|
|
30
31
|
Requires-Dist: strands-agents[ollama]
|
|
31
|
-
Requires-Dist: strands-agents[openai]
|
|
32
|
-
Requires-Dist: strands-agents[anthropic]
|
|
33
32
|
Requires-Dist: strands-agents-tools
|
|
34
|
-
Requires-Dist: strands-
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist:
|
|
33
|
+
Requires-Dist: strands-agentcore-tools
|
|
34
|
+
Requires-Dist: beautifulsoup4
|
|
35
|
+
Requires-Dist: colorama
|
|
37
36
|
Requires-Dist: websockets
|
|
38
|
-
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: strands-agents[openai]; extra == "all"
|
|
39
|
+
Requires-Dist: strands-agents[anthropic]; extra == "all"
|
|
40
|
+
Requires-Dist: strands-fun-tools[audio]; extra == "all"
|
|
41
|
+
Requires-Dist: strands-fun-tools[vision]; extra == "all"
|
|
42
|
+
Requires-Dist: strands-fun-tools[all]; extra == "all"
|
|
43
|
+
Requires-Dist: strands-mcp-server; extra == "all"
|
|
39
44
|
Dynamic: license-file
|
|
40
45
|
|
|
41
46
|
# 🦆 DevDuck
|
|
@@ -47,10 +52,14 @@ Minimalist AI agent that fixes itself when things break.
|
|
|
47
52
|
## Install
|
|
48
53
|
|
|
49
54
|
```bash
|
|
55
|
+
# Minimal install
|
|
50
56
|
pipx install devduck
|
|
57
|
+
|
|
58
|
+
# Full install (all tools)
|
|
59
|
+
pipx install "devduck[all]"
|
|
51
60
|
```
|
|
52
61
|
|
|
53
|
-
Requires: Python 3.10+, Ollama running
|
|
62
|
+
Requires: Python 3.10+, Ollama running (or set MODEL_PROVIDER)
|
|
54
63
|
|
|
55
64
|
## Use
|
|
56
65
|
|
|
@@ -7,10 +7,14 @@ Minimalist AI agent that fixes itself when things break.
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
+
# Minimal install
|
|
10
11
|
pipx install devduck
|
|
12
|
+
|
|
13
|
+
# Full install (all tools)
|
|
14
|
+
pipx install "devduck[all]"
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
Requires: Python 3.10+, Ollama running
|
|
17
|
+
Requires: Python 3.10+, Ollama running (or set MODEL_PROVIDER)
|
|
14
18
|
|
|
15
19
|
## Use
|
|
16
20
|
|
devduck-0.3.0/action.yml
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
name: 'DevDuck'
|
|
2
|
+
description: 'Quack.'
|
|
3
|
+
author: 'Cagatay Cali'
|
|
4
|
+
|
|
5
|
+
branding:
|
|
6
|
+
icon: 'bot'
|
|
7
|
+
color: 'blue'
|
|
8
|
+
|
|
9
|
+
inputs:
|
|
10
|
+
# Core Task Configuration
|
|
11
|
+
task:
|
|
12
|
+
description: 'Specific task for the agent to perform'
|
|
13
|
+
required: true
|
|
14
|
+
|
|
15
|
+
system_prompt:
|
|
16
|
+
description: 'Additional system prompt instructions for the agent'
|
|
17
|
+
required: false
|
|
18
|
+
default: ''
|
|
19
|
+
|
|
20
|
+
# Model Configuration
|
|
21
|
+
provider:
|
|
22
|
+
description: 'Model provider (bedrock, openai, github, anthropic, litellm, llamaapi)'
|
|
23
|
+
required: false
|
|
24
|
+
default: 'bedrock'
|
|
25
|
+
|
|
26
|
+
model:
|
|
27
|
+
description: 'Model ID to use'
|
|
28
|
+
required: false
|
|
29
|
+
default: 'us.anthropic.claude-sonnet-4-20250514-v1:0'
|
|
30
|
+
|
|
31
|
+
max_tokens:
|
|
32
|
+
description: 'Maximum tokens for the model'
|
|
33
|
+
required: false
|
|
34
|
+
default: '32768'
|
|
35
|
+
|
|
36
|
+
temperature:
|
|
37
|
+
description: 'Temperature for model generation (0.0-1.0)'
|
|
38
|
+
required: false
|
|
39
|
+
default: '1'
|
|
40
|
+
|
|
41
|
+
# Tool Configuration
|
|
42
|
+
tools:
|
|
43
|
+
description: 'Comma-separated list of tools to enable'
|
|
44
|
+
required: false
|
|
45
|
+
default: 'current_time'
|
|
46
|
+
|
|
47
|
+
# Authentication & Access
|
|
48
|
+
github_token:
|
|
49
|
+
description: 'GitHub token for API access'
|
|
50
|
+
required: false
|
|
51
|
+
default: ${{ github.token }}
|
|
52
|
+
|
|
53
|
+
# AWS Configuration (for Bedrock)
|
|
54
|
+
aws_region:
|
|
55
|
+
description: 'AWS region for Bedrock'
|
|
56
|
+
required: false
|
|
57
|
+
default: 'us-west-2'
|
|
58
|
+
|
|
59
|
+
aws_role_arn:
|
|
60
|
+
description: 'AWS role ARN for OIDC authentication'
|
|
61
|
+
required: false
|
|
62
|
+
|
|
63
|
+
# Knowledge Base Configuration
|
|
64
|
+
knowledge_base_id:
|
|
65
|
+
description: 'Strands Knowledge Base ID for memory'
|
|
66
|
+
required: false
|
|
67
|
+
|
|
68
|
+
# Advanced Configuration
|
|
69
|
+
additional_request_fields:
|
|
70
|
+
description: 'Additional request fields JSON (e.g., thinking, anthropic_beta)'
|
|
71
|
+
required: false
|
|
72
|
+
default: ''
|
|
73
|
+
|
|
74
|
+
bypass_tool_consent:
|
|
75
|
+
description: 'Skip tool consent prompts'
|
|
76
|
+
required: false
|
|
77
|
+
default: 'true'
|
|
78
|
+
|
|
79
|
+
# MCP Configuration
|
|
80
|
+
mcp_servers:
|
|
81
|
+
description: 'MCP servers configuration (JSON string)'
|
|
82
|
+
required: false
|
|
83
|
+
# default: '{"mcpServers":{"strands-docs":{"command":"uvx","args":["strands-agents-mcp-server"],"timeout":30000}}}'
|
|
84
|
+
|
|
85
|
+
# Agent Runner Configuration
|
|
86
|
+
agent_runner:
|
|
87
|
+
description: 'URL to custom agent_runner.py script. Falls back to repository variable AGENT_RUNNER, then default gist'
|
|
88
|
+
required: false
|
|
89
|
+
|
|
90
|
+
outputs:
|
|
91
|
+
result:
|
|
92
|
+
description: 'Result from the agent execution'
|
|
93
|
+
|
|
94
|
+
success:
|
|
95
|
+
description: 'Whether the agent execution was successful'
|
|
96
|
+
|
|
97
|
+
error:
|
|
98
|
+
description: 'Error message if execution failed'
|
|
99
|
+
|
|
100
|
+
runs:
|
|
101
|
+
using: 'composite'
|
|
102
|
+
steps:
|
|
103
|
+
- name: Use Homebrew Python 3.13
|
|
104
|
+
if: runner.os == 'macOS'
|
|
105
|
+
shell: bash
|
|
106
|
+
run: |
|
|
107
|
+
echo "/opt/homebrew/opt/python@3.13/libexec/bin" >> $GITHUB_PATH
|
|
108
|
+
echo "/opt/homebrew/bin" >> $GITHUB_PATH
|
|
109
|
+
|
|
110
|
+
- name: Set up Python
|
|
111
|
+
if: runner.os != 'macOS'
|
|
112
|
+
uses: actions/setup-python@v4
|
|
113
|
+
with:
|
|
114
|
+
python-version: '3.13'
|
|
115
|
+
|
|
116
|
+
- name: Cache UV dependencies
|
|
117
|
+
uses: actions/cache@v4
|
|
118
|
+
with:
|
|
119
|
+
path: |
|
|
120
|
+
~/.cache/uv
|
|
121
|
+
key: ${{ runner.os }}-python${{ matrix.python-version || '3.13' }}-uv-${{ hashFiles('requirements.txt', 'requirements-local.txt') }}-v2
|
|
122
|
+
restore-keys: |
|
|
123
|
+
${{ runner.os }}-python${{ matrix.python-version || '3.13' }}-uv-
|
|
124
|
+
|
|
125
|
+
- name: Clean UV cache on failure
|
|
126
|
+
if: failure()
|
|
127
|
+
shell: bash
|
|
128
|
+
run: |
|
|
129
|
+
uv cache clean || true
|
|
130
|
+
rm -rf ~/.cache/uv/* || true
|
|
131
|
+
|
|
132
|
+
- name: Install UV
|
|
133
|
+
shell: bash
|
|
134
|
+
run: |
|
|
135
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
136
|
+
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
|
137
|
+
|
|
138
|
+
- name: Install Strands Agents
|
|
139
|
+
shell: bash
|
|
140
|
+
run: |
|
|
141
|
+
uv pip install --system -r requirements.txt --quiet
|
|
142
|
+
|
|
143
|
+
- name: Install Local Dependencies
|
|
144
|
+
if: runner.os == 'macOS'
|
|
145
|
+
shell: bash
|
|
146
|
+
run: |
|
|
147
|
+
uv pip install --system -r requirements-local.txt --quiet
|
|
148
|
+
|
|
149
|
+
- name: Configure Git
|
|
150
|
+
shell: bash
|
|
151
|
+
run: |
|
|
152
|
+
git config --global user.name "DevDuck" 2>/dev/null
|
|
153
|
+
git config --global user.email "hey@strands.my" 2>/dev/null
|
|
154
|
+
|
|
155
|
+
- name: Configure AWS credentials
|
|
156
|
+
if: inputs.aws_role_arn != ''
|
|
157
|
+
uses: aws-actions/configure-aws-credentials@v4
|
|
158
|
+
with:
|
|
159
|
+
role-to-assume: ${{ inputs.aws_role_arn }}
|
|
160
|
+
role-session-name: GitHubActions-StrandsAgent-${{ github.run_id }}
|
|
161
|
+
aws-region: ${{ inputs.aws_region }}
|
|
162
|
+
mask-aws-account-id: true
|
|
163
|
+
|
|
164
|
+
- name: Download agent script
|
|
165
|
+
shell: bash
|
|
166
|
+
env:
|
|
167
|
+
# GitHub Configuration - Priority: Input > Repository Variable > Default Gist
|
|
168
|
+
AGENT_RUNNER: ${{ inputs.agent_runner || 'https://gist.githubusercontent.com/cagataycali/f67df59c34c0d7ff13adbba3b0f8d3d9/raw/bb324e37d19948f0d36d0ca9ce721ca64deb13a1/agent_runner.py' }}
|
|
169
|
+
run: |
|
|
170
|
+
curl -s -o agent_runner.py $AGENT_RUNNER
|
|
171
|
+
|
|
172
|
+
- name: Run Strands Agent
|
|
173
|
+
shell: bash
|
|
174
|
+
env:
|
|
175
|
+
# GitHub Configuration
|
|
176
|
+
GITHUB_TOKEN: ${{ inputs.github_token }}
|
|
177
|
+
GITHUB_CONTEXT: ${{ toJson(github) }}
|
|
178
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
179
|
+
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
|
180
|
+
GITHUB_ACTOR: ${{ github.actor }}
|
|
181
|
+
|
|
182
|
+
# Model Configuration
|
|
183
|
+
STRANDS_PROVIDER: ${{ inputs.provider }}
|
|
184
|
+
STRANDS_MODEL_ID: ${{ inputs.model }}
|
|
185
|
+
STRANDS_MAX_TOKENS: ${{ inputs.max_tokens }}
|
|
186
|
+
STRANDS_TEMPERATURE: ${{ inputs.temperature }}
|
|
187
|
+
STRANDS_ADDITIONAL_REQUEST_FIELDS: ${{ inputs.additional_request_fields }}
|
|
188
|
+
|
|
189
|
+
# Tool Configuration
|
|
190
|
+
STRANDS_TOOLS: ${{ inputs.tools }}
|
|
191
|
+
BYPASS_TOOL_CONSENT: ${{ inputs.bypass_tool_consent }}
|
|
192
|
+
STRANDS_TOOL_CONSOLE_MODE: "enabled"
|
|
193
|
+
|
|
194
|
+
# Task Configuration
|
|
195
|
+
INPUT_TASK: ${{ inputs.task }}
|
|
196
|
+
INPUT_SYSTEM_PROMPT: ${{ inputs.system_prompt }}
|
|
197
|
+
|
|
198
|
+
# AWS Configuration
|
|
199
|
+
AWS_REGION: ${{ inputs.aws_region }}
|
|
200
|
+
|
|
201
|
+
# Knowledge Base
|
|
202
|
+
STRANDS_KNOWLEDGE_BASE_ID: ${{ inputs.knowledge_base_id }}
|
|
203
|
+
|
|
204
|
+
# MCP Configuration
|
|
205
|
+
MCP_SERVERS: ${{ inputs.mcp_servers }}
|
|
206
|
+
run: |
|
|
207
|
+
AGENT_INPUT="$INPUT_TASK"
|
|
208
|
+
|
|
209
|
+
echo "🤖 Starting Strands Agent..."
|
|
210
|
+
|
|
211
|
+
# Run the agent with UV
|
|
212
|
+
uv run agent_runner.py "$AGENT_INPUT"
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Minimal DevDuck GitHub Agent Runner
|
|
4
|
+
Uses DevDuck with GitHub context, MCP tools, and knowledge base integration.
|
|
5
|
+
|
|
6
|
+
Examples:
|
|
7
|
+
echo "analyze this data" | python agent_runner.py
|
|
8
|
+
python agent_runner.py "what can you do"
|
|
9
|
+
INPUT_TASK="search papers" python agent_runner.py
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import datetime
|
|
16
|
+
|
|
17
|
+
# Disable auto-start servers for DevDuck singleton before import
|
|
18
|
+
os.environ["DEVDUCK_AUTO_START_SERVERS"] = "false"
|
|
19
|
+
os.environ["BYPASS_TOOL_CONSENT"] = "true"
|
|
20
|
+
os.environ["STRANDS_TOOL_CONSOLE_MODE"] = "enabled"
|
|
21
|
+
|
|
22
|
+
from mcp import StdioServerParameters, stdio_client
|
|
23
|
+
from strands.tools.mcp import MCPClient
|
|
24
|
+
|
|
25
|
+
# HTTP transport imports
|
|
26
|
+
try:
|
|
27
|
+
from mcp.client.sse import sse_client
|
|
28
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
29
|
+
|
|
30
|
+
HTTP_TRANSPORT_AVAILABLE = True
|
|
31
|
+
except ImportError:
|
|
32
|
+
HTTP_TRANSPORT_AVAILABLE = False
|
|
33
|
+
|
|
34
|
+
# DevDuck is required
|
|
35
|
+
from devduck import DevDuck
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_mcp_config() -> dict:
|
|
39
|
+
"""Load MCP server configuration from JSON file or environment variable."""
|
|
40
|
+
mcp_servers_env = os.getenv("MCP_SERVERS")
|
|
41
|
+
if mcp_servers_env:
|
|
42
|
+
try:
|
|
43
|
+
config = json.loads(mcp_servers_env)
|
|
44
|
+
return config.get("mcpServers", {})
|
|
45
|
+
except json.JSONDecodeError:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
# Fallback: hardcoded strands-agents-mcp-server
|
|
49
|
+
return {"strands-agents": {"command": "uvx", "args": ["strands-agents-mcp-server"]}}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def create_mcp_clients() -> list:
|
|
53
|
+
"""Create MCP clients from configuration."""
|
|
54
|
+
mcp_config = load_mcp_config()
|
|
55
|
+
clients = []
|
|
56
|
+
|
|
57
|
+
for server_name, server_config in mcp_config.items():
|
|
58
|
+
try:
|
|
59
|
+
command = server_config.get("command")
|
|
60
|
+
if command:
|
|
61
|
+
args = server_config.get("args", [])
|
|
62
|
+
env = server_config.get("env", {})
|
|
63
|
+
|
|
64
|
+
client = MCPClient(
|
|
65
|
+
lambda cmd=command, arguments=args, environment=env: stdio_client(
|
|
66
|
+
StdioServerParameters(
|
|
67
|
+
command=cmd,
|
|
68
|
+
args=arguments,
|
|
69
|
+
env=environment if environment else None,
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
clients.append((server_name, client))
|
|
74
|
+
|
|
75
|
+
elif server_config.get("url") and HTTP_TRANSPORT_AVAILABLE:
|
|
76
|
+
url = server_config.get("url")
|
|
77
|
+
headers = server_config.get("headers", {})
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
client = MCPClient(lambda: sse_client(url, headers=headers))
|
|
81
|
+
clients.append((server_name, client))
|
|
82
|
+
except Exception:
|
|
83
|
+
try:
|
|
84
|
+
client = MCPClient(
|
|
85
|
+
lambda: streamablehttp_client(url, headers=headers)
|
|
86
|
+
)
|
|
87
|
+
clients.append((server_name, client))
|
|
88
|
+
except Exception:
|
|
89
|
+
continue
|
|
90
|
+
except Exception:
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
return clients
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def build_system_prompt() -> str:
|
|
97
|
+
"""Build system prompt from environment variables."""
|
|
98
|
+
base_prompt = os.getenv("SYSTEM_PROMPT", "")
|
|
99
|
+
if not base_prompt:
|
|
100
|
+
base_prompt = "You are an autonomous GitHub agent powered by DevDuck."
|
|
101
|
+
|
|
102
|
+
input_system_prompt = os.getenv("INPUT_SYSTEM_PROMPT", "")
|
|
103
|
+
if input_system_prompt:
|
|
104
|
+
base_prompt = f"{base_prompt}\n\n{input_system_prompt}"
|
|
105
|
+
|
|
106
|
+
github_context = os.environ.get("GITHUB_CONTEXT", "")
|
|
107
|
+
if github_context:
|
|
108
|
+
base_prompt = f"{base_prompt}\n\nGitHub Context:\n{github_context}"
|
|
109
|
+
|
|
110
|
+
return base_prompt
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def run_agent(query: str) -> str:
|
|
114
|
+
"""Run DevDuck agent with GitHub context and MCP tools."""
|
|
115
|
+
|
|
116
|
+
print("🦆 Creating DevDuck agent...")
|
|
117
|
+
|
|
118
|
+
# Create a new DevDuck instance without auto-starting servers
|
|
119
|
+
duck = DevDuck(auto_start_servers=False)
|
|
120
|
+
agent = duck.agent
|
|
121
|
+
|
|
122
|
+
# Build and append GitHub context to DevDuck's system prompt
|
|
123
|
+
github_system_prompt = build_system_prompt()
|
|
124
|
+
if agent and github_system_prompt:
|
|
125
|
+
agent.system_prompt += "\n\nCustom system prompt:" + github_system_prompt
|
|
126
|
+
print("✅ GitHub context appended to DevDuck's system prompt")
|
|
127
|
+
|
|
128
|
+
# Get MCP clients
|
|
129
|
+
mcp_clients = create_mcp_clients()
|
|
130
|
+
|
|
131
|
+
# Run agent with MCP context
|
|
132
|
+
# NOTE: KB retrieval/storage now handled automatically in DevDuck.__call__
|
|
133
|
+
result = None
|
|
134
|
+
if mcp_clients:
|
|
135
|
+
print(f"🔗 Loading {len(mcp_clients)} MCP servers...")
|
|
136
|
+
|
|
137
|
+
def run_with_mcp(clients, current_agent):
|
|
138
|
+
if not clients:
|
|
139
|
+
return current_agent(query)
|
|
140
|
+
|
|
141
|
+
server_name, client = clients[0]
|
|
142
|
+
remaining = clients[1:]
|
|
143
|
+
|
|
144
|
+
with client:
|
|
145
|
+
try:
|
|
146
|
+
mcp_tools = client.list_tools_sync()
|
|
147
|
+
print(f"✓ {server_name}: {len(mcp_tools)} tools loaded")
|
|
148
|
+
except Exception as e:
|
|
149
|
+
print(f"⚠️ {server_name} failed: {e}")
|
|
150
|
+
|
|
151
|
+
return run_with_mcp(remaining, current_agent)
|
|
152
|
+
|
|
153
|
+
result = run_with_mcp(mcp_clients, agent)
|
|
154
|
+
else:
|
|
155
|
+
result = agent(query)
|
|
156
|
+
|
|
157
|
+
return result
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def main():
|
|
161
|
+
"""Main entry point."""
|
|
162
|
+
|
|
163
|
+
# Multi-input task collection (priority-based)
|
|
164
|
+
tasks = {}
|
|
165
|
+
|
|
166
|
+
# Priority 1: Piped input (stdin)
|
|
167
|
+
if not sys.stdin.isatty():
|
|
168
|
+
try:
|
|
169
|
+
pipe_task = sys.stdin.read().strip()
|
|
170
|
+
if pipe_task:
|
|
171
|
+
tasks["pipe"] = pipe_task
|
|
172
|
+
except Exception:
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
# Priority 2: Command line arguments
|
|
176
|
+
if len(sys.argv) > 1:
|
|
177
|
+
cmd_task = " ".join(sys.argv[1:])
|
|
178
|
+
if cmd_task:
|
|
179
|
+
tasks["command_line"] = cmd_task
|
|
180
|
+
|
|
181
|
+
# Priority 3: Environment variable
|
|
182
|
+
env_task = os.getenv("INPUT_TASK")
|
|
183
|
+
if env_task:
|
|
184
|
+
tasks["environment"] = env_task
|
|
185
|
+
|
|
186
|
+
# No tasks found
|
|
187
|
+
if not tasks:
|
|
188
|
+
print(
|
|
189
|
+
"❌ Error: No task provided. Use command line args, stdin, or INPUT_TASK env var."
|
|
190
|
+
)
|
|
191
|
+
sys.exit(1)
|
|
192
|
+
|
|
193
|
+
# Combine all tasks
|
|
194
|
+
combined_task = "\n\n".join(tasks.values())
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
result = run_agent(combined_task)
|
|
198
|
+
print(f"\n✅ Agent Execution Complete!")
|
|
199
|
+
print(f"📝 Result: {result}")
|
|
200
|
+
except Exception as e:
|
|
201
|
+
print(f"❌ Fatal error: {e}")
|
|
202
|
+
sys.exit(1)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
if __name__ == "__main__":
|
|
206
|
+
main()
|