[READ-ONLY] Mirror of https://github.com/danielroe/errx. Zero dependency library to capture and parse stack traces in Node, Bun, Deno and more.
error
stack-trace
1name: agent-scan
2
3on:
4 # zizmor: ignore[dangerous-triggers] - DO NOT add action/checkout in this workflow as it uses pull_request_target
5 pull_request_target:
6 types:
7 - opened
8 - reopened
9
10concurrency:
11 group: agent-scan-${{ github.event.pull_request.number }}
12 cancel-in-progress: true
13
14permissions:
15 issues: write
16 pull-requests: write
17
18jobs:
19 agentscan:
20 runs-on: ubuntu-latest
21 steps:
22 - name: AgentScan
23 id: agentscan
24 uses: MatteoGabriele/agentscan-action@c7d61446e7aece6bdd3edcee4558bbfc0392615e # v2.0.1
25 with:
26 github-token: ${{ secrets.GITHUB_TOKEN }}
27 mode: silent
28 - name: Handle flagged PR
29 if: contains(fromJSON('["automation","mixed"]'), steps.agentscan.outputs.classification) || steps.agentscan.outputs.community-flagged == 'true'
30 env:
31 CLASSIFICATION: ${{ steps.agentscan.outputs.classification }}
32 COMMUNITY_FLAGGED: ${{ steps.agentscan.outputs.community-flagged }}
33 uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
34 with:
35 script: |
36 const prNumber = context.payload.pull_request.number;
37 const classification = process.env.CLASSIFICATION;
38 const communityFlagged = process.env.COMMUNITY_FLAGGED === 'true';
39 const shouldClose = classification === 'automation' || communityFlagged;
40
41 const issue = context.payload.pull_request
42 const labels = issue.labels?.map(l => l.name) || []
43
44 if (!labels.includes('possible bot')) {
45 await github.rest.issues.addLabels({
46 owner: context.repo.owner,
47 repo: context.repo.repo,
48 issue_number: prNumber,
49 labels: ['possible bot'],
50 })
51 }
52
53 const comments = await github.paginate(github.rest.issues.listComments, {
54 owner: context.repo.owner,
55 repo: context.repo.repo,
56 issue_number: prNumber,
57 per_page: 100,
58 })
59
60 const alreadyCommented = comments.some(
61 c => c.user.type === 'Bot' && c.body.includes('AI-assisted contribution guidelines')
62 )
63
64 if (!alreadyCommented) {
65 const closingNote = shouldClose
66 ? "We're closing this for now as the account looks automated. If we got that wrong, please just reopen the PR and we'll take another look."
67 : 'If this was flagged in error, we apologise! 😳 Just let us know. 🙏'
68
69 await github.rest.issues.createComment({
70 owner: context.repo.owner,
71 repo: context.repo.repo,
72 issue_number: prNumber,
73 body: [
74 "We've flagged this as a potential contribution without a human behind it. We welcome the thoughtful use of AI tools when contributing, but ask all contributors to follow [two core principles](https://roe.dev/blog/using-ai-in-open-source):",
75 '',
76 '1. **Never let an LLM speak for you** - all comments, issues, and PR descriptions should be written in your own words, reflecting your own understanding.',
77 '2. **Never let an LLM think for you** - only submit contributions you fully understand and can explain.',
78 '',
79 'Please review these AI-assisted contribution guidelines and update this contribution if needed.',
80 '',
81 closingNote,
82 ].join('\n'),
83 })
84 } else {
85 core.info('Possible-bot comment already exists - skipping comment.')
86 }
87
88 if (shouldClose && issue.state === 'open' && !alreadyCommented) {
89 await github.rest.pulls.update({
90 owner: context.repo.owner,
91 repo: context.repo.repo,
92 pull_number: prNumber,
93 state: 'closed',
94 title: '🚨 unwelcome pr from bot 🚨',
95 })
96 }
97
98 const actionTaken = [
99 'Added `possible bot` label',
100 alreadyCommented ? null : 'posted policy comment',
101 shouldClose && !alreadyCommented ? 'closed PR' : null,
102 ].filter(Boolean).join(', ')
103
104 core.summary
105 .addHeading('AgentScan: Possible Bot Flag', 2)
106 .addTable([
107 [{ data: 'Property', header: true }, { data: 'Value', header: true }],
108 ['Pull Request', `#${prNumber}`],
109 ['Classification', classification],
110 ['Community flagged', String(communityFlagged)],
111 ['Action', actionTaken || 'No action (already handled)'],
112 ])
113
114 await core.summary.write()