1+ # Issue Management Workflow
2+ # Comprehensive automation for GitHub issues lifecycle
3+
4+ name : Issue Management
5+
6+ on :
7+ issues :
8+ types : [opened, labeled, assigned, closed, reopened]
9+ issue_comment :
10+ types : [created]
11+
12+ jobs :
13+ # Process new issues with automation
14+ process-new-issue :
15+ name : Process New Issue
16+ runs-on : ubuntu-latest
17+ if : github.event.action == 'opened'
18+
19+ steps :
20+ - name : Checkout repository
21+ uses : actions/checkout@v4
22+
23+ - name : Auto-assign to repository owner
24+ uses : actions/github-script@v7
25+ with :
26+ script : |
27+ const issue = context.payload.issue;
28+ const owner = context.repo.owner;
29+
30+ // Auto-assign to repository owner for single-developer repo
31+ await github.rest.issues.addAssignees({
32+ owner: context.repo.owner,
33+ repo: context.repo.repo,
34+ issue_number: issue.number,
35+ assignees: [owner]
36+ });
37+
38+ console.log(`Auto-assigned issue #${issue.number} to ${owner}`);
39+
40+ - name : Add priority labels based on content
41+ uses : actions/github-script@v7
42+ with :
43+ script : |
44+ const issue = context.payload.issue;
45+ const title = issue.title.toLowerCase();
46+ const body = issue.body ? issue.body.toLowerCase() : '';
47+ const content = title + ' ' + body;
48+
49+ const labelsToAdd = [];
50+
51+ // Priority detection
52+ if (content.includes('critical') || content.includes('urgent') || content.includes('security')) {
53+ labelsToAdd.push('priority:high');
54+ } else if (content.includes('important') || content.includes('breaking')) {
55+ labelsToAdd.push('priority:medium');
56+ }
57+
58+ // Category detection beyond template labels
59+ if (content.includes('performance') || content.includes('slow') || content.includes('timeout')) {
60+ labelsToAdd.push('category:performance');
61+ }
62+ if (content.includes('security') || content.includes('vulnerability')) {
63+ labelsToAdd.push('category:security');
64+ }
65+ if (content.includes('test') || content.includes('testing') || content.includes('coverage')) {
66+ labelsToAdd.push('category:testing');
67+ }
68+
69+ // Add labels if any were detected
70+ if (labelsToAdd.length > 0) {
71+ await github.rest.issues.addLabels({
72+ owner: context.repo.owner,
73+ repo: context.repo.repo,
74+ issue_number: issue.number,
75+ labels: labelsToAdd
76+ });
77+
78+ console.log(`Added labels: ${labelsToAdd.join(', ')} to issue #${issue.number}`);
79+ }
80+
81+ - name : Welcome first-time contributors
82+ uses : actions/github-script@v7
83+ with :
84+ script : |
85+ const issue = context.payload.issue;
86+ const issueAuthor = issue.user.login;
87+
88+ // Check if this is the author's first issue
89+ const issues = await github.rest.issues.listForRepo({
90+ owner: context.repo.owner,
91+ repo: context.repo.repo,
92+ creator: issueAuthor,
93+ state: 'all'
94+ });
95+
96+ // If this is their first issue (and not the repo owner)
97+ if (issues.data.length === 1 && issueAuthor !== context.repo.owner) {
98+ await github.rest.issues.createComment({
99+ owner: context.repo.owner,
100+ repo: context.repo.repo,
101+ issue_number: issue.number,
102+ body: `👋 Welcome to the Agent Communication MCP Server project, @${issueAuthor}!
103+
104+ Thank you for taking the time to create this ${issue.labels.find(l => l.name === 'bug') ? 'bug report' : issue.labels.find(l => l.name === 'enhancement') ? 'feature request' : 'issue'}.
105+
106+ 🔍 **What happens next:**
107+ - Your issue has been automatically assigned and labeled
108+ - I'll review it and provide feedback within 24-48 hours
109+ - If it's a bug, I'll prioritize it based on severity
110+ - If it's a feature request, I'll evaluate it against the project roadmap
111+
112+ 📚 **Helpful resources:**
113+ - [Contributing Guide](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/CONTRIBUTING.md)
114+ - [Protocol Documentation](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/PROTOCOL.md)
115+
116+ Thanks for contributing to making AI agent coordination better! 🚀`
117+ });
118+
119+ console.log(`Welcomed first-time contributor : ${issueAuthor}`);
120+ }
121+
122+ # Handle issue status updates
123+ update-issue-status :
124+ name : Update Issue Status
125+ runs-on : ubuntu-latest
126+ if : github.event.action == 'labeled' || github.event.action == 'closed' || github.event.action == 'reopened'
127+
128+ steps :
129+ - name : Update status based on labels
130+ uses : actions/github-script@v7
131+ with :
132+ script : |
133+ const issue = context.payload.issue;
134+
135+ if (github.event.action === 'labeled') {
136+ const label = context.payload.label;
137+
138+ // Remove needs-triage when other priority labels are added
139+ if (label.name.startsWith('priority:')) {
140+ const labels = issue.labels.map(l => l.name);
141+ if (labels.includes('needs-triage')) {
142+ await github.rest.issues.removeLabel({
143+ owner: context.repo.owner,
144+ repo: context.repo.repo,
145+ issue_number: issue.number,
146+ name: 'needs-triage'
147+ });
148+
149+ console.log(`Removed needs-triage label from issue #${issue.number}`);
150+ }
151+ }
152+
153+ // Add in-progress label if assigned to someone
154+ if (label.name === 'in-progress' || issue.assignees.length > 0) {
155+ const labels = issue.labels.map(l => l.name);
156+ if (!labels.includes('in-progress') && !labels.includes('completed')) {
157+ await github.rest.issues.addLabels({
158+ owner: context.repo.owner,
159+ repo: context.repo.repo,
160+ issue_number: issue.number,
161+ labels: ['in-progress']
162+ });
163+ }
164+ }
165+ }
166+
167+ // Handle issue closure
168+ if (github.event.action === 'closed') {
169+ await github.rest.issues.addLabels({
170+ owner: context.repo.owner,
171+ repo: context.repo.repo,
172+ issue_number: issue.number,
173+ labels: ['completed']
174+ });
175+
176+ // Remove in-progress label
177+ try {
178+ await github.rest.issues.removeLabel({
179+ owner: context.repo.owner,
180+ repo: context.repo.repo,
181+ issue_number: issue.number,
182+ name: 'in-progress'
183+ });
184+ } catch (error) {
185+ // Label might not exist, ignore error
186+ console.log(`Could not remove in-progress label: ${error.message}`);
187+ }
188+ }
189+
190+ // Handle issue reopening
191+ if (github.event.action === 'reopened') {
192+ // Remove completed label and add back in-progress
193+ try {
194+ await github.rest.issues.removeLabel({
195+ owner: context.repo.owner,
196+ repo: context.repo.repo,
197+ issue_number: issue.number,
198+ name: 'completed'
199+ });
200+ } catch (error) {
201+ console.log(`Could not remove completed label: ${error.message}`);
202+ }
203+
204+ await github.rest.issues.addLabels({
205+ owner: context.repo.owner,
206+ repo: context.repo.repo,
207+ issue_number: issue.number,
208+ labels: ['in-progress']
209+ });
210+ }
211+
212+ # Handle issue comments for commands
213+ process-issue-commands :
214+ name : Process Issue Commands
215+ runs-on : ubuntu-latest
216+ if : github.event.action == 'created' && github.event.issue != null
217+
218+ steps :
219+ - name : Process commands in comments
220+ uses : actions/github-script@v7
221+ with :
222+ script : |
223+ const comment = context.payload.comment;
224+ const issue = context.payload.issue;
225+ const commenter = comment.user.login;
226+
227+ // Only repo owner can execute commands
228+ if (commenter !== context.repo.owner) {
229+ return;
230+ }
231+
232+ const body = comment.body.trim();
233+
234+ // /priority <level> command
235+ if (body.match(/^\/priority (high|medium|low)$/)) {
236+ const level = body.match(/^\/priority (high|medium|low)$/)[1];
237+
238+ // Remove existing priority labels
239+ const priorityLabels = ['priority:high', 'priority:medium', 'priority:low'];
240+ const currentLabels = issue.labels.map(l => l.name);
241+
242+ for (const label of priorityLabels) {
243+ if (currentLabels.includes(label)) {
244+ await github.rest.issues.removeLabel({
245+ owner: context.repo.owner,
246+ repo: context.repo.repo,
247+ issue_number: issue.number,
248+ name: label
249+ });
250+ }
251+ }
252+
253+ // Add new priority label
254+ await github.rest.issues.addLabels({
255+ owner: context.repo.owner,
256+ repo: context.repo.repo,
257+ issue_number: issue.number,
258+ labels: [`priority:${level}`]
259+ });
260+
261+ await github.rest.issues.createComment({
262+ owner: context.repo.owner,
263+ repo: context.repo.repo,
264+ issue_number: issue.number,
265+ body: `✅ Priority set to **${level}**`
266+ });
267+ }
268+
269+ // /branch command - create development branch
270+ if (body === '/branch') {
271+ const branchName = `issue-${issue.number}-${issue.title.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').substring(0, 50)}`;
272+
273+ await github.rest.issues.createComment({
274+ owner: context.repo.owner,
275+ repo: context.repo.repo,
276+ issue_number: issue.number,
277+ body: `🌿 **Development branch suggestion:**
278+
279+ \`\`\`bash
280+ gh issue develop ${issue.number} --name ${branchName}
281+ # or manually:
282+ git checkout -b ${branchName}
283+ \`\`\`
284+
285+ This will create a feature branch linked to this issue. When you create a PR from this branch, it will automatically reference this issue.`
286+ });
287+ }
288+
289+ // /close [reason] command
290+ if (body.match(/^\/close/)) {
291+ const reason = body.replace(/^\/close\s*/, '') || 'Resolved by maintainer';
292+
293+ await github.rest.issues.createComment({
294+ owner : context.repo.owner,
295+ repo : context.repo.repo,
296+ issue_number : issue.number,
297+ body : ` 🔒 **Closing issue:** ${reason}`
298+ });
299+
300+ await github.rest.issues.update({
301+ owner : context.repo.owner,
302+ repo : context.repo.repo,
303+ issue_number : issue.number,
304+ state : ' closed' ,
305+ state_reason : ' completed'
306+ });
307+ }
0 commit comments