@yemi33/minions 0.1.614 → 0.1.616
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -1
- package/engine/cleanup.js +39 -25
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.616 (2026-04-08)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- audit fixes for _killProcessInWorktree
|
|
7
|
+
|
|
8
|
+
## 0.1.615 (2026-04-08)
|
|
4
9
|
|
|
5
10
|
### Features
|
|
6
11
|
- clickable pinned notes and skills with full content + doc-chat
|
|
7
12
|
|
|
8
13
|
### Fixes
|
|
14
|
+
- only kill orphaned processes before worktree removal, not active ones
|
|
9
15
|
- kill process holding worktree lock before removal attempt
|
|
10
16
|
- dedup worktree removal attempts, add Windows rd fallback
|
|
11
17
|
- accurate LLM call timing and agent metrics on engine page
|
package/engine/cleanup.js
CHANGED
|
@@ -40,39 +40,52 @@ function worktreeDirMatchesBranch(dirLower, branch) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
* Kill
|
|
44
|
-
*
|
|
43
|
+
* Kill orphaned processes whose dispatch ID appears in the worktree dir name.
|
|
44
|
+
* Only kills processes NOT in the active dispatch queue — never kills live agents.
|
|
45
45
|
*/
|
|
46
|
-
function _killProcessInWorktree(dir, activeProcesses) {
|
|
46
|
+
function _killProcessInWorktree(dir, activeProcesses, activeDispatchIds) {
|
|
47
47
|
const dirLower = dir.toLowerCase();
|
|
48
|
-
|
|
48
|
+
const activeIds = activeDispatchIds;
|
|
49
|
+
|
|
50
|
+
// Check tracked in-memory processes — only kill if dispatch is no longer active
|
|
49
51
|
for (const [id, info] of activeProcesses.entries()) {
|
|
50
|
-
if (dirLower.includes(id.toLowerCase().slice(-8)))
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
if (!dirLower.includes(id.toLowerCase().slice(-8))) continue;
|
|
53
|
+
if (activeIds.has(id)) continue; // still active — do not kill
|
|
54
|
+
try { shared.killImmediate(info.proc); } catch {}
|
|
55
|
+
activeProcesses.delete(id);
|
|
56
|
+
log('info', `Killed orphaned process for dispatch ${id} before worktree removal`);
|
|
54
57
|
}
|
|
55
|
-
|
|
58
|
+
|
|
59
|
+
// Check PID files in engine/tmp/ — only kill if no active dispatch matches
|
|
56
60
|
try {
|
|
57
61
|
const tmpDir = path.join(ENGINE_DIR, 'tmp');
|
|
58
62
|
for (const f of fs.readdirSync(tmpDir)) {
|
|
59
63
|
if (!f.startsWith('pid-') || !f.endsWith('.pid')) continue;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
const pidFileName = f.replace(/^pid-/, '').replace(/\.pid$/, '');
|
|
65
|
+
if (!dirLower.includes(pidFileName.slice(-8))) continue;
|
|
66
|
+
// Verify this PID file's dispatch is not active
|
|
67
|
+
const isActive = [...activeIds].some(id => pidFileName.includes(id.slice(-8)));
|
|
68
|
+
if (isActive) continue; // still active — do not kill
|
|
69
|
+
const pid = parseInt(fs.readFileSync(path.join(tmpDir, f), 'utf8').trim(), 10);
|
|
70
|
+
if (pid > 0) {
|
|
71
|
+
// Verify the process is actually a node/claude process before killing
|
|
72
|
+
try {
|
|
73
|
+
if (process.platform === 'win32') {
|
|
74
|
+
const taskInfo = exec(`tasklist /FI "PID eq ${pid}" /NH`, { encoding: 'utf8', timeout: 3000, windowsHide: true });
|
|
75
|
+
if (!taskInfo.toLowerCase().includes('node')) continue; // not a node process — skip
|
|
76
|
+
exec(`taskkill /F /PID ${pid}`, { stdio: 'pipe', timeout: 5000, windowsHide: true });
|
|
77
|
+
} else {
|
|
78
|
+
// Verify it's a node process before killing (prevent recycled PID kill)
|
|
79
|
+
try {
|
|
80
|
+
const psOut = exec(`ps -p ${pid} -o comm=`, { encoding: 'utf8', timeout: 3000 }).trim();
|
|
81
|
+
if (!psOut.includes('node') && !psOut.includes('claude')) continue;
|
|
82
|
+
} catch { continue; } // process dead or ps failed
|
|
83
|
+
process.kill(pid, 'SIGKILL');
|
|
84
|
+
}
|
|
85
|
+
log('info', `Killed orphaned PID ${pid} (${f}) before worktree removal`);
|
|
86
|
+
} catch {} // process may already be dead
|
|
75
87
|
}
|
|
88
|
+
try { fs.unlinkSync(path.join(tmpDir, f)); } catch {}
|
|
76
89
|
}
|
|
77
90
|
} catch {} // tmp dir may not exist
|
|
78
91
|
}
|
|
@@ -191,6 +204,7 @@ function runCleanup(config, verbose = false) {
|
|
|
191
204
|
|
|
192
205
|
const wtEntries = []; // { dir, wtPath, mtime, shouldClean, isProtected }
|
|
193
206
|
const dispatch = getDispatch();
|
|
207
|
+
const activeDispatchIds = new Set((dispatch.active || []).map(d => d.id));
|
|
194
208
|
|
|
195
209
|
for (const { dir, wtPath } of allDirs) {
|
|
196
210
|
|
|
@@ -303,7 +317,7 @@ function runCleanup(config, verbose = false) {
|
|
|
303
317
|
if (_attemptedWorktreePaths.has(entry.wtPath)) continue;
|
|
304
318
|
_attemptedWorktreePaths.add(entry.wtPath);
|
|
305
319
|
// Kill any process still running in this worktree before removal
|
|
306
|
-
_killProcessInWorktree(entry.dir, activeProcesses);
|
|
320
|
+
_killProcessInWorktree(entry.dir, activeProcesses, activeDispatchIds);
|
|
307
321
|
if (shared.removeWorktree(entry.wtPath, root, worktreeRoot)) {
|
|
308
322
|
cleaned.worktrees++;
|
|
309
323
|
if (verbose) console.log(` Removed worktree: ${entry.wtPath}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.616",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|