-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathghar-issue-command-executor.yml
More file actions
203 lines (182 loc) · 8.65 KB
/
Copy pathghar-issue-command-executor.yml
File metadata and controls
203 lines (182 loc) · 8.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
name: Issues - Execute OpenCode Commands from Issue Comments
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
parse-and-check:
# Only run if the comment author is the repository owner
if: github.event.comment.author_association == 'OWNER'
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
contents: write
outputs:
has-command: ${{ steps.parse-comment.outputs.has_command }}
has-agent: ${{ steps.parse-comment.outputs.has_agent }}
command: ${{ steps.parse-comment.outputs.command }}
prompt: ${{ steps.parse-comment.outputs.prompt }}
agent: ${{ steps.parse-comment.outputs.agent }}
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: React with eyes emoji
uses: actions/github-script@v9
with:
script: |
// Handle both issue comments and PR review comments
const commentId = context.eventName === 'pull_request_review_comment'
? context.payload.comment.id
: context.payload.comment.id;
if (context.eventName === 'pull_request_review_comment') {
await github.rest.reactions.createForPullRequestReviewComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
content: 'eyes'
});
} else {
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
content: 'eyes'
});
}
- name: Parse comment for commands
id: parse-comment
run: |
comment="${{ github.event.comment.body }}"
echo "Original comment: $comment"
# Check if comment contains a command or agent command
if [[ "$comment" =~ ^\*([a-zA-Z0-9_-]+)[[:space:]]+/([a-zA-Z0-9_-]+)(.*)$ ]]; then
# Agent command format: *agent /command PROMPT
agent="${BASH_REMATCH[1]}"
command="${BASH_REMATCH[2]}"
prompt="${BASH_REMATCH[3]}"
# Remove leading whitespace from prompt
prompt=$(echo "$prompt" | sed 's/^[[:space:]]*//')
# "agent" is a generic placeholder — clear it to trigger the "build" fallback
[[ "$agent" == "agent" ]] && agent=""
echo "Detected agent command"
echo "agent=$agent" >> $GITHUB_OUTPUT
echo "command=$command" >> $GITHUB_OUTPUT
echo "prompt<<EOF" >> $GITHUB_OUTPUT
echo "$prompt" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "has_command=true" >> $GITHUB_OUTPUT
echo "has_agent=true" >> $GITHUB_OUTPUT
elif [[ "$comment" =~ ^\*([a-zA-Z0-9_-]+)[[:space:]]+(.*)$ ]]; then
# Agent prompt format: *agent PROMPT (no slash command)
agent="${BASH_REMATCH[1]}"
prompt="${BASH_REMATCH[2]}"
# Remove leading whitespace from prompt
prompt=$(echo "$prompt" | sed 's/^[[:space:]]*//')
# "agent" is a generic placeholder — clear it to trigger the "build" fallback
[[ "$agent" == "agent" ]] && agent=""
echo "Detected agent prompt"
echo "agent=$agent" >> $GITHUB_OUTPUT
echo "prompt<<EOF" >> $GITHUB_OUTPUT
echo "$prompt" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "has_command=false" >> $GITHUB_OUTPUT
echo "has_agent=true" >> $GITHUB_OUTPUT
elif [[ "$comment" =~ ^/([a-zA-Z0-9_-]+)(.*)$ ]]; then
# Direct command format: /command PROMPT
command="${BASH_REMATCH[1]}"
prompt="${BASH_REMATCH[2]}"
# Remove leading whitespace from prompt
prompt=$(echo "$prompt" | sed 's/^[[:space:]]*//')
echo "Detected direct command"
echo "command=$command" >> $GITHUB_OUTPUT
echo "prompt<<EOF" >> $GITHUB_OUTPUT
echo "$prompt" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "has_command=true" >> $GITHUB_OUTPUT
echo "has_agent=false" >> $GITHUB_OUTPUT
else
echo "No command detected in comment"
echo "has_command=false" >> $GITHUB_OUTPUT
echo "has_agent=false" >> $GITHUB_OUTPUT
fi
execute-opencode:
if: needs.parse-and-check.outputs.has-command == 'true' || needs.parse-and-check.outputs.has-agent == 'true'
needs: parse-and-check
permissions:
issues: write
pull-requests: write
contents: write
uses: ./.github/workflows/core-opencode-run.yml
with:
run-name: "${{ github.event.issue.number != null && format('issue-{0}-comment-{1}', github.event.issue.number, github.event.comment.id) || format('pr-{0}-comment-{1}', github.event.pull_request.number, github.event.comment.id) }}"
command: ${{ needs.parse-and-check.outputs.command }}
prompt: ${{ needs.parse-and-check.outputs.prompt }}
agent: ${{ needs.parse-and-check.outputs.agent || 'build' }}
timeout-minutes: 30
comment-results:
if: (needs.parse-and-check.outputs.has-command == 'true' || needs.parse-and-check.outputs.has-agent == 'true') && always()
needs: [parse-and-check, execute-opencode]
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Comment execution results
uses: actions/github-script@v9
with:
script: |
const hasCommand = '${{ needs.parse-and-check.outputs.has-command }}' === 'true';
const hasAgent = '${{ needs.parse-and-check.outputs.has-agent }}' === 'true';
const command = '${{ needs.parse-and-check.outputs.command }}';
const agent = '${{ needs.parse-and-check.outputs.agent }}';
const runId = context.runId;
// Use toJSON() to properly escape the results content
const results = ${{ toJSON(needs.execute-opencode.outputs.results) }};
const resultsTruncated = '${{ needs.execute-opencode.outputs.results-truncated }}' === 'true';
const artifactName = '${{ needs.execute-opencode.outputs.artifact-name }}';
let executionType;
if (hasAgent && hasCommand) {
executionType = '*' + agent + ' /' + command;
} else if (hasAgent) {
executionType = '*' + agent;
} else {
executionType = '/' + command;
}
let commentBody;
if (results && results.trim() !== '' && results !== '❌ No results file generated') {
const truncationNote = resultsTruncated ?
'\n\n📎 **Full results available in artifact:** [' + artifactName + '](' + context.payload.repository.html_url + '/actions/runs/' + runId + ')' : '';
commentBody = '🤖 **OpenCode Execution Complete**\n\n' +
'**Command:** `' + executionType + '`\n' +
'**Run ID:** ' + runId + '\n' +
'**Status:** ✅ Completed\n\n' +
'## Results\n\n' +
results + truncationNote;
} else {
commentBody = '🤖 **OpenCode Execution Complete**\n\n' +
'**Command:** `' + executionType + '`\n' +
'**Run ID:** ' + runId + '\n' +
'**Status:** ⚠️ No output generated\n\n' +
'📎 **Check the full logs:** [View run details](' + context.payload.repository.html_url + '/actions/runs/' + runId + ')';
}
// Handle both issue comments and PR review comments
if (context.eventName === 'pull_request_review_comment') {
return await github.rest.pulls.createReviewComment({
pull_number: context.payload.pull_request.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody,
commit_id: context.payload.pull_request.head.sha,
path: context.payload.comment.path,
line: context.payload.comment.line
});
} else {
return await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
}