Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/strands-agent-runner/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ runs:
STRANDS_TOOL_CONSOLE_MODE: 'enabled'
BYPASS_TOOL_CONSENT: 'true'
run: |
uv run --no-project ${{ runner.temp }}/strands-agent-runner/.github/scripts/python/agent_runner.py "$INPUT_TASK"
uv run --no-project ${{ runner.temp }}/strands-agent-runner/.github/scripts/python/agent_runner.py

- name: Capture repository state
shell: bash
Expand Down
464 changes: 325 additions & 139 deletions .github/agent-sops/task-release-notes.sop.md

Large diffs are not rendered by default.

24 changes: 22 additions & 2 deletions .github/scripts/javascript/process-input.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ async function getIssueInfo(github, context, inputs) {
const issueId = context.eventName === 'workflow_dispatch'
? inputs.issue_id
: context.payload.issue.number.toString();
const commentBody = context.payload.comment?.body || '';
const command = context.eventName === 'workflow_dispatch'
? inputs.command
: (context.payload.comment.body.match(/^\/strands\s*(.*?)$/m)?.[1]?.trim() || '');
: (commentBody.startsWith('/strands') ? commentBody.slice('/strands'.length).trim() : '');

console.log(`Event: ${context.eventName}, Issue ID: ${issueId}, Command: "${command}"`);

Expand Down Expand Up @@ -76,10 +77,29 @@ function buildPrompts(mode, issueId, isPullRequest, command, branchName, inputs)
const scriptFile = scriptFiles[mode] || scriptFiles['refiner'];
const systemPrompt = fs.readFileSync(scriptFile, 'utf8');

// Extract the user's feedback/instructions after the mode keyword
// e.g., "release-notes Move #123 to Major Features" -> "Move #123 to Major Features"
const modeKeywords = {
'release-notes': /^(?:release-notes|release notes)\s*/i,
'implementer': /^implement\s*/i,
'refiner': /^refine\s*/i
};

const modePattern = modeKeywords[mode];
const userFeedback = modePattern ? command.replace(modePattern, '').trim() : command.trim();

let prompt = (isPullRequest)
? 'The pull request id is:'
: 'The issue id is:';
prompt += `${issueId}\n${command}\nreview and continue`;
prompt += `${issueId}\n`;

// If there's substantial user feedback beyond just the command keyword, include it as the main instruction
// Otherwise, use the default "review and continue" for initial triggers
if (userFeedback && userFeedback.length > 0) {
prompt += userFeedback;
} else {
prompt += 'review and continue';
}

return { sessionId, systemPrompt, prompt };
}
Expand Down
13 changes: 6 additions & 7 deletions .github/scripts/python/agent_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,12 @@ def run_agent(query: str):
def main() -> None:
"""Main entry point for the agent runner."""
try:
# Read task from command line arguments
if len(sys.argv) < 2:
raise ValueError("Task argument is required")

task = " ".join(sys.argv[1:])
if not task.strip():
raise ValueError("Task cannot be empty")
# Prefer INPUT_TASK env var (avoids shell escaping issues), fall back to CLI args
task = os.getenv("INPUT_TASK", "").strip()
if not task and len(sys.argv) > 1:
task = " ".join(sys.argv[1:]).strip()
if not task:
raise ValueError("Task is required (via INPUT_TASK env var or CLI argument)")
print(f"🤖 Running agent with task: {task}")

run_agent(task)
Expand Down
33 changes: 31 additions & 2 deletions .github/scripts/python/handoff_to_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,29 @@
from strands.types.tools import ToolContext
from strands_tools.utils import console_util

from github_tools import add_issue_comment


@tool(context=True)
def handoff_to_user(message: str, tool_context: ToolContext) -> str:
def handoff_to_user(
message: str,
tool_context: ToolContext,
post_comment: bool,
issue_number: int | None = None,
) -> str:
"""
Hand off control to the user with a message.
Hand off control to the user with a message. This stops the agent execution
and waits for the user to respond before continuing.

Args:
message: The message to give to the user
post_comment: If true, post the message as a comment on the GitHub issue/PR.
Only set this to true when user intervention or feedback is required
before continuing (e.g., clarification needed, approval required,
or a decision must be made). Do not post a comment for simple status updates
or completion messages. If you are asking a question to the user this MUST
be true.
issue_number: The issue or PR number to comment on (required if post_comment is true)

Returns:
The users response after handing back control
Expand All @@ -25,6 +41,19 @@ def handoff_to_user(message: str, tool_context: ToolContext) -> str:
)
)

# Post comment to GitHub if requested
if post_comment:
if issue_number is None:
console.print(
Panel(
"Cannot post comment: issue_number is required when post_comment is true",
title="[bold red]Error",
border_style="red",
)
)
else:
add_issue_comment(issue_number, message)

request_state = {
"stop_event_loop": True
}
Expand Down
Loading