openAgent
gitlabThe open source UNIVERSAL agent.
clone
https://gitlab.com/bitzzdev/openAgent.gitAIagentautomationopen-sourceuniversal-agent
#!/usr/bin/env bunconst repo = "anomalyco/opencode"const days = 60const msg = `To stay organized issues are automatically closed after ${days} days of no activity. If the issue is still relevant please open a new one.`const token = process.env.GITHUB_TOKENif (!token) {console.error("GITHUB_TOKEN environment variable is required")process.exit(1)}const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000)const agentLogin = "opencode-agent[bot]"const teamMembers = new Set((await Bun.file(new URL("../../.github/TEAM_MEMBERS", import.meta.url)).text()).split("\n").map((line) => line.trim().toLowerCase()).filter(Boolean),)const teamAssociations = new Set(["OWNER", "MEMBER"])type Issue = {number: numberupdated_at: stringauthor_association: stringuser: { login: string } | null}const headers = {Authorization: `Bearer ${token}`,"Content-Type": "application/json",Accept: "application/vnd.github+json","X-GitHub-Api-Version": "2022-11-28",}function shouldSkip(i: Issue) {const login = i.user?.login.toLowerCase()return login === agentLogin || (login ? teamMembers.has(login) : false) || teamAssociations.has(i.author_association)}async function close(num: number) {const base = `https://api.github.com/repos/${repo}/issues/${num}`const comment = await fetch(`${base}/comments`, {method: "POST",headers,body: JSON.stringify({ body: msg }),})if (!comment.ok) throw new Error(`Failed to comment #${num}: ${comment.status} ${comment.statusText}`)const patch = await fetch(base, {method: "PATCH",headers,body: JSON.stringify({ state: "closed", state_reason: "not_planned" }),})if (!patch.ok) throw new Error(`Failed to close #${num}: ${patch.status} ${patch.statusText}`)console.log(`Closed https://github.com/${repo}/issues/${num}`)}async function main() {let page = 1let closed = 0while (true) {const res = await fetch(`https://api.github.com/repos/${repo}/issues?state=open&sort=updated&direction=asc&per_page=100&page=${page}`,{ headers },)if (!res.ok) throw new Error(res.statusText)const all = (await res.json()) as Issue[]if (all.length === 0) breakconsole.log(`Fetched page ${page} ${all.length} issues`)const stale: number[] = []for (const i of all) {const updated = new Date(i.updated_at)if (updated < cutoff) {if (shouldSkip(i)) {console.log(`Skipping stale issue #${i.number}; author ${i.user?.login ?? "unknown"} is exempt`)continue}stale.push(i.number)} else {console.log(`\nFound fresh issue #${i.number}, stopping`)if (stale.length > 0) {for (const num of stale) {await close(num)closed++}}console.log(`Closed ${closed} issues total`)return}}if (stale.length > 0) {for (const num of stale) {await close(num)closed++}}page++}console.log(`Closed ${closed} issues total`)}main().catch((err) => {console.error("Error:", err)process.exit(1)})