Add close-inactive workflow; enable plugin configs

Add a GitHub Actions workflow (.github/workflows/close-inactive.yml) that searches for open issues and PRs with no activity past a configurable cutoff and optionally posts a closing comment and closes them. The workflow runs on a daily cron and supports manual dispatch with inputs for inactive_days and dry_run; it uses actions/github-script to paginate search results and handle closing. Also update plugins/text_editor/plugin.yaml to enable per_project_config and per_agent_config (changed from false to true) so the text_editor plugin can be configured per project and per agent.

frdel committed Mar 3, 2026 at 18:50 UTC e2a7df7d33528e38be35f75eb9c953a6e2671f66
2 files changed +110 -2
.github/workflows/close-inactive.yml new
+108
@@ -0,0 +1,108 @@
1 +name: Close inactive issues and PRs
2 +
3 +on:
4 + schedule:
5 + - cron: "17 3 * * *"
6 + workflow_dispatch:
7 + inputs:
8 + inactive_days:
9 + description: "Close items with no activity for more than N days"
10 + required: false
11 + default: "90"
12 + dry_run:
13 + description: "If true, only print URLs (no comment/close)"
14 + required: false
15 + default: "true"
16 +
17 +permissions:
18 + issues: write
19 + pull-requests: write
20 +
21 +env:
22 + DEFAULT_INACTIVE_DAYS: "90"
23 + DEFAULT_DRY_RUN: "true"
24 +
25 +jobs:
26 + close_inactive:
27 + if: github.repository == 'agent0ai/agent-zero' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
28 + runs-on: ubuntu-latest
29 + steps:
30 + - name: Find and optionally close inactive issues/PRs
31 + uses: actions/github-script@v7
32 + env:
33 + INACTIVE_DAYS: ${{ github.event_name == 'workflow_dispatch' && inputs.inactive_days || env.DEFAULT_INACTIVE_DAYS }}
34 + DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || env.DEFAULT_DRY_RUN }}
35 + with:
36 + script: |
37 + const inactiveDaysRaw = process.env.INACTIVE_DAYS ?? "90";
38 + const inactiveDays = Number.parseInt(inactiveDaysRaw, 10);
39 + if (!Number.isFinite(inactiveDays) || inactiveDays <= 0) {
40 + core.setFailed(`Invalid INACTIVE_DAYS: ${inactiveDaysRaw}`);
41 + return;
42 + }
43 +
44 + const dryRunRaw = (process.env.DRY_RUN ?? "true").toLowerCase();
45 + const dryRun = ["1", "true", "yes", "y"].includes(dryRunRaw);
46 +
47 + const now = new Date();
48 + const cutoff = new Date(now.getTime() - inactiveDays * 24 * 60 * 60 * 1000);
49 + const cutoffDate = cutoff.toISOString().slice(0, 10);
50 +
51 + core.info(`inactiveDays=${inactiveDays}`);
52 + core.info(`dryRun=${dryRun}`);
53 + core.info(`cutoffDate=${cutoffDate}`);
54 +
55 + const owner = context.repo.owner;
56 + const repo = context.repo.repo;
57 +
58 + async function processQuery(kind, searchQuery) {
59 + core.info(`Searching ${kind}: ${searchQuery}`);
60 +
61 + const items = await github.paginate(github.rest.search.issuesAndPullRequests, {
62 + q: searchQuery,
63 + per_page: 100,
64 + });
65 +
66 + if (items.length === 0) {
67 + core.info(`No inactive ${kind} found.`);
68 + return;
69 + }
70 +
71 + core.info(`Found ${items.length} inactive ${kind}. URLs:`);
72 + for (const item of items) {
73 + core.info(item.html_url);
74 + }
75 +
76 + if (dryRun) {
77 + return;
78 + }
79 +
80 + for (const item of items) {
81 + const issueNumber = item.number;
82 + const url = item.html_url;
83 +
84 + try {
85 + await github.rest.issues.createComment({
86 + owner,
87 + repo,
88 + issue_number: issueNumber,
89 + body: `Closing due to inactivity of ${inactiveDays} days.`,
90 + });
91 +
92 + await github.rest.issues.update({
93 + owner,
94 + repo,
95 + issue_number: issueNumber,
96 + state: "closed",
97 + });
98 +
99 + core.info(`Closed: ${url}`);
100 + } catch (err) {
101 + core.warning(`Failed to close ${url}: ${err?.message ?? String(err)}`);
102 + }
103 + }
104 + }
105 +
106 + const base = `repo:${owner}/${repo} is:open updated:<${cutoffDate}`;
107 + await processQuery("issues", `${base} is:issue`);
108 + await processQuery("pull requests", `${base} is:pr`);
plugins/text_editor/plugin.yaml
+2 -2
@@ -3,5 +3,5 @@ description: Native tool to read, write, and patch text files in an LLM-friendly
3 version: 1.0.0
4 settings_sections:
5 - agent
6 -per_project_config: false
7 -per_agent_config: false
6 +per_project_config: true
7 +per_agent_config: true