Skip to content

Commit fc5a4dc

Browse files
ryanmacclaude
andcommitted
fix: Apply black formatting and update macOS runner version
- Applied black formatting to all Python files in .conductor/scripts/ and setup.py - Updated CI workflow to use macos-14 instead of macos-latest to avoid deprecation warnings - Merged latest changes from main branch including dependency-check.py improvements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent af82fe8 commit fc5a4dc

File tree

11 files changed

+959
-745
lines changed

11 files changed

+959
-745
lines changed

.conductor/scripts/archive-completed.py

Lines changed: 181 additions & 113 deletions
Large diffs are not rendered by default.

.conductor/scripts/cleanup-stale.py

Lines changed: 123 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ def run_gh_command(self, args):
1717
"""Run GitHub CLI command and return output"""
1818
try:
1919
result = subprocess.run(
20-
["gh"] + args,
21-
capture_output=True,
22-
text=True,
23-
check=True
20+
["gh"] + args, capture_output=True, text=True, check=True
2421
)
2522
return result.stdout.strip()
2623
except subprocess.CalledProcessError as e:
@@ -32,18 +29,26 @@ def run_gh_command(self, args):
3229

3330
def get_assigned_issues(self):
3431
"""Get all assigned conductor task issues"""
35-
output = self.run_gh_command([
36-
"issue", "list",
37-
"-l", "conductor:task",
38-
"--state", "open",
39-
"--assignee", "*", # Issues with any assignee
40-
"--limit", "1000",
41-
"--json", "number,title,assignees,labels"
42-
])
43-
32+
output = self.run_gh_command(
33+
[
34+
"issue",
35+
"list",
36+
"-l",
37+
"conductor:task",
38+
"--state",
39+
"open",
40+
"--assignee",
41+
"*", # Issues with any assignee
42+
"--limit",
43+
"1000",
44+
"--json",
45+
"number,title,assignees,labels",
46+
]
47+
)
48+
4449
if not output:
4550
return []
46-
51+
4752
try:
4853
return json.loads(output)
4954
except json.JSONDecodeError:
@@ -52,35 +57,44 @@ def get_assigned_issues(self):
5257
def check_issue_staleness(self, issue_number):
5358
"""Check if an issue is stale based on comment activity"""
5459
# Get recent comments
55-
comments_output = self.run_gh_command([
56-
"issue", "view", str(issue_number),
57-
"--json", "comments",
58-
"--jq", '.comments[-10:] | reverse | .[]' # Last 10 comments, newest first
59-
])
60-
60+
comments_output = self.run_gh_command(
61+
[
62+
"issue",
63+
"view",
64+
str(issue_number),
65+
"--json",
66+
"comments",
67+
"--jq",
68+
".comments[-10:] | reverse | .[]", # Last 10 comments, newest first
69+
]
70+
)
71+
6172
if not comments_output:
6273
return True, None, None # Consider stale if no comments
63-
74+
6475
current_time = datetime.utcnow()
6576
latest_activity = None
6677
agent_info = None
67-
78+
6879
# Parse comments to find latest activity
69-
for line in comments_output.strip().split('\n'):
80+
for line in comments_output.strip().split("\n"):
7081
if line:
7182
try:
7283
comment = json.loads(line)
7384
body = comment.get("body", "")
74-
85+
7586
# Check for agent activity (heartbeat, progress, etc.)
76-
if any(marker in body for marker in ["Agent Claimed Task", "Heartbeat", "Progress"]):
87+
if any(
88+
marker in body
89+
for marker in ["Agent Claimed Task", "Heartbeat", "Progress"]
90+
):
7791
comment_time = datetime.fromisoformat(
7892
comment["createdAt"].replace("Z", "+00:00")
7993
).replace(tzinfo=None)
80-
94+
8195
if not latest_activity or comment_time > latest_activity:
8296
latest_activity = comment_time
83-
97+
8498
# Try to extract agent info
8599
if "```json" in body:
86100
json_start = body.find("```json") + 7
@@ -93,40 +107,50 @@ def check_issue_staleness(self, issue_number):
93107
pass
94108
except (json.JSONDecodeError, KeyError):
95109
continue
96-
110+
97111
if latest_activity:
98112
is_stale = (current_time - latest_activity) > self.timeout
99113
return is_stale, latest_activity, agent_info
100-
114+
101115
return True, None, None # Stale if no activity found
102116

103117
def clean_stale_issue(self, issue):
104118
"""Clean up a stale issue by unassigning and removing in-progress label"""
105119
issue_number = issue["number"]
106120
issue_title = issue["title"]
107-
121+
108122
# Check staleness
109123
is_stale, last_activity, agent_id = self.check_issue_staleness(issue_number)
110-
124+
111125
if not is_stale:
112126
return False
113-
127+
114128
print(f"🧹 Cleaning stale issue #{issue_number}: {issue_title}")
115-
129+
116130
# Remove all assignees
117131
if issue.get("assignees"):
118132
for assignee in issue["assignees"]:
119-
self.run_gh_command([
120-
"issue", "edit", str(issue_number),
121-
"--remove-assignee", assignee["login"]
122-
])
123-
133+
self.run_gh_command(
134+
[
135+
"issue",
136+
"edit",
137+
str(issue_number),
138+
"--remove-assignee",
139+
assignee["login"],
140+
]
141+
)
142+
124143
# Remove in-progress label
125-
self.run_gh_command([
126-
"issue", "edit", str(issue_number),
127-
"--remove-label", "conductor:in-progress"
128-
])
129-
144+
self.run_gh_command(
145+
[
146+
"issue",
147+
"edit",
148+
str(issue_number),
149+
"--remove-label",
150+
"conductor:in-progress",
151+
]
152+
)
153+
130154
# Add comment explaining the cleanup
131155
cleanup_comment = f"""### 🧹 Task Released - Stale Agent
132156
@@ -138,64 +162,76 @@ def clean_stale_issue(self, issue):
138162
139163
The task is now available for other agents to claim.
140164
"""
141-
142-
self.run_gh_command([
143-
"issue", "comment", str(issue_number),
144-
"--body", cleanup_comment
145-
])
146-
147-
self.cleaned_agents.append({
148-
"issue_number": issue_number,
149-
"task": issue_title,
150-
"agent_id": agent_id,
151-
"last_activity": last_activity.isoformat() if last_activity else "Unknown"
152-
})
153-
165+
166+
self.run_gh_command(
167+
["issue", "comment", str(issue_number), "--body", cleanup_comment]
168+
)
169+
170+
self.cleaned_agents.append(
171+
{
172+
"issue_number": issue_number,
173+
"task": issue_title,
174+
"agent_id": agent_id,
175+
"last_activity": (
176+
last_activity.isoformat() if last_activity else "Unknown"
177+
),
178+
}
179+
)
180+
154181
return True
155182

156183
def clean_stale_work(self):
157184
"""Remove stale agent work from issues"""
158185
print("🔍 Checking for stale agents...")
159-
186+
160187
# Get all assigned issues
161188
assigned_issues = self.get_assigned_issues()
162-
189+
163190
if not assigned_issues:
164191
print("✅ No assigned tasks found")
165192
return True
166-
193+
167194
print(f"📋 Found {len(assigned_issues)} assigned tasks")
168-
195+
169196
# Check each issue for staleness
170197
cleaned_count = 0
171198
for issue in assigned_issues:
172199
if self.clean_stale_issue(issue):
173200
cleaned_count += 1
174-
201+
175202
if cleaned_count > 0:
176203
print(f"\n✅ Cleaned up {cleaned_count} stale tasks")
177204
for agent in self.cleaned_agents:
178205
print(f" - Issue #{agent['issue_number']}: {agent['task']}")
179-
print(f" Agent: {agent['agent_id']}, Last activity: {agent['last_activity']}")
206+
print(
207+
f" Agent: {agent['agent_id']}, Last activity: {agent['last_activity']}"
208+
)
180209
else:
181210
print("✅ No stale agents found")
182-
211+
183212
return True
184213

185214
def update_status_issue(self):
186215
"""Update the status issue with cleanup information"""
187216
if not self.cleaned_agents:
188217
return
189-
218+
190219
# Find or create status issue
191-
output = self.run_gh_command([
192-
"issue", "list",
193-
"-l", "conductor:status",
194-
"--state", "open",
195-
"--limit", "1",
196-
"--json", "number"
197-
])
198-
220+
output = self.run_gh_command(
221+
[
222+
"issue",
223+
"list",
224+
"-l",
225+
"conductor:status",
226+
"--state",
227+
"open",
228+
"--limit",
229+
"1",
230+
"--json",
231+
"number",
232+
]
233+
)
234+
199235
status_issue_number = None
200236
if output:
201237
try:
@@ -204,7 +240,7 @@ def update_status_issue(self):
204240
status_issue_number = issues[0]["number"]
205241
except json.JSONDecodeError:
206242
pass
207-
243+
208244
if status_issue_number:
209245
# Add cleanup report as comment
210246
cleanup_report = f"""### 🧹 Stale Agent Cleanup Report
@@ -215,16 +251,17 @@ def update_status_issue(self):
215251
#### Released Tasks:
216252
"""
217253
for agent in self.cleaned_agents:
218-
cleanup_report += f"- **Issue #{agent['issue_number']}**: {agent['task']}\n"
254+
cleanup_report += (
255+
f"- **Issue #{agent['issue_number']}**: {agent['task']}\n"
256+
)
219257
cleanup_report += f" - Agent: `{agent['agent_id']}`\n"
220258
cleanup_report += f" - Last activity: {agent['last_activity']}\n"
221-
259+
222260
cleanup_report += "\n---\n*Automated cleanup by Code-Conductor*"
223-
224-
self.run_gh_command([
225-
"issue", "comment", str(status_issue_number),
226-
"--body", cleanup_report
227-
])
261+
262+
self.run_gh_command(
263+
["issue", "comment", str(status_issue_number), "--body", cleanup_report]
264+
)
228265

229266

230267
def main():
@@ -238,27 +275,27 @@ def main():
238275
parser.add_argument(
239276
"--dry-run",
240277
action="store_true",
241-
help="Show what would be cleaned without making changes"
278+
help="Show what would be cleaned without making changes",
242279
)
243280

244281
args = parser.parse_args()
245282

246283
cleaner = StaleCleaner(timeout_minutes=args.timeout)
247-
284+
248285
# Check GitHub CLI authentication
249286
if not cleaner.run_gh_command(["auth", "status"]):
250287
print("❌ GitHub CLI not authenticated. Run 'gh auth login' first.")
251288
sys.exit(1)
252-
289+
253290
# Run cleanup
254291
success = cleaner.clean_stale_work()
255-
292+
256293
if success and cleaner.cleaned_agents:
257294
# Update status issue with cleanup report
258295
cleaner.update_status_issue()
259-
296+
260297
sys.exit(0 if success else 1)
261298

262299

263300
if __name__ == "__main__":
264-
main()
301+
main()

0 commit comments

Comments
 (0)