@yemi33/minions 0.1.608 → 0.1.610

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 CHANGED
@@ -1,6 +1,14 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.608 (2026-04-08)
3
+ ## 0.1.610 (2026-04-08)
4
+
5
+ ### Fixes
6
+ - worktree count now checks for .git file instead of any directory
7
+
8
+ ## 0.1.609 (2026-04-08)
9
+
10
+ ### Fixes
11
+ - show lifetime completed count instead of capped dispatch window
4
12
 
5
13
  ### Other
6
14
  - docs: add CC/doc-chat flow section, fix stale claims in CLAUDE.md
package/dashboard.js CHANGED
@@ -176,7 +176,24 @@ function _countWorktrees() {
176
176
  const root = p.localPath ? path.resolve(p.localPath) : null;
177
177
  if (!root) continue;
178
178
  const wtRoot = path.resolve(root, config.engine?.worktreeRoot || shared.ENGINE_DEFAULTS.worktreeRoot);
179
- try { count += fs.readdirSync(wtRoot).filter(f => fs.statSync(path.join(wtRoot, f)).isDirectory()).length; } catch {}
179
+ try {
180
+ for (const dir of fs.readdirSync(wtRoot)) {
181
+ const dirPath = path.join(wtRoot, dir);
182
+ try { if (!fs.statSync(dirPath).isDirectory()) continue; } catch { continue; }
183
+ // A git worktree has a .git file (not directory) in its root
184
+ if (fs.existsSync(path.join(dirPath, '.git'))) {
185
+ count++;
186
+ } else {
187
+ // Parent directory — scan subdirs for nested worktrees
188
+ try {
189
+ for (const sub of fs.readdirSync(dirPath)) {
190
+ const subPath = path.join(dirPath, sub);
191
+ try { if (fs.statSync(subPath).isDirectory() && fs.existsSync(path.join(subPath, '.git'))) count++; } catch {}
192
+ }
193
+ } catch {}
194
+ }
195
+ }
196
+ } catch {}
180
197
  }
181
198
  return count;
182
199
  } catch { return 0; }
package/engine/cli.js CHANGED
@@ -556,7 +556,9 @@ const commands = {
556
556
  }
557
557
 
558
558
  console.log('');
559
- console.log(`Dispatch: ${dispatch.pending.length} pending | ${(dispatch.active || []).length} active | ${(dispatch.completed || []).length} completed`);
559
+ const metrics = shared.safeJson(path.join(__dirname, 'metrics.json')) || {};
560
+ const lifetimeCompleted = Object.entries(metrics).filter(([k]) => !k.startsWith('_')).reduce((sum, [, m]) => sum + (m.tasksCompleted || 0) + (m.tasksErrored || 0), 0);
561
+ console.log(`Dispatch: ${dispatch.pending.length} pending | ${(dispatch.active || []).length} active | ${lifetimeCompleted} completed`);
560
562
  console.log(`Active processes: ${e.activeProcesses.size}`);
561
563
 
562
564
  const metricsPath = path.join(ENGINE_DIR, 'metrics.json');
package/engine/queries.js CHANGED
@@ -94,7 +94,9 @@ function invalidateDispatchCache() { _dispatchCache = null; _dispatchCacheAt = 0
94
94
  function getDispatchQueue() {
95
95
  const d = getDispatch();
96
96
  const allCompleted = d.completed || [];
97
- d.completedTotal = allCompleted.length;
97
+ // Lifetime total from metrics (dispatch.completed is capped at 100)
98
+ const metrics = safeJson(path.join(ENGINE_DIR, 'metrics.json')) || {};
99
+ d.completedTotal = Object.entries(metrics).filter(([k]) => !k.startsWith('_')).reduce((sum, [, m]) => sum + (m.tasksCompleted || 0) + (m.tasksErrored || 0), 0);
98
100
  d.completed = allCompleted.slice(-20);
99
101
  return d;
100
102
  }
@@ -122,7 +124,54 @@ function getEngineLog() {
122
124
  }
123
125
 
124
126
  function getMetrics() {
125
- return safeJson(path.join(ENGINE_DIR, 'metrics.json')) || {};
127
+ const metrics = safeJson(path.join(ENGINE_DIR, 'metrics.json')) || {};
128
+
129
+ // Enrich agent PR counts from pull-requests.json (source of truth)
130
+ const allPrs = getPullRequests();
131
+ const prCountByAgent = {};
132
+ const prApprovedByAgent = {};
133
+ const prRejectedByAgent = {};
134
+ for (const pr of allPrs) {
135
+ const agent = (pr.agent || '').toLowerCase();
136
+ if (!agent || agent.startsWith('temp-')) continue;
137
+ prCountByAgent[agent] = (prCountByAgent[agent] || 0) + 1;
138
+ if (pr.reviewStatus === 'approved' || pr.status === 'merged') prApprovedByAgent[agent] = (prApprovedByAgent[agent] || 0) + 1;
139
+ if (pr.reviewStatus === 'rejected') prRejectedByAgent[agent] = (prRejectedByAgent[agent] || 0) + 1;
140
+ }
141
+
142
+ // Enrich agent runtime from completed dispatch entries
143
+ const dispatch = getDispatch();
144
+ const runtimeByAgent = {};
145
+ const runtimeCountByAgent = {};
146
+ for (const d of (dispatch.completed || [])) {
147
+ const agent = d.agent;
148
+ if (!agent || agent.startsWith('temp-')) continue;
149
+ if (d.started_at && d.completed_at) {
150
+ const ms = new Date(d.completed_at).getTime() - new Date(d.started_at).getTime();
151
+ if (ms > 0) {
152
+ runtimeByAgent[agent] = (runtimeByAgent[agent] || 0) + ms;
153
+ runtimeCountByAgent[agent] = (runtimeCountByAgent[agent] || 0) + 1;
154
+ }
155
+ }
156
+ }
157
+
158
+ // Apply enrichments to agent metrics
159
+ for (const [agentId, m] of Object.entries(metrics)) {
160
+ if (agentId.startsWith('_')) continue;
161
+ const lower = agentId.toLowerCase();
162
+ if (prCountByAgent[lower] !== undefined) {
163
+ m.prsCreated = prCountByAgent[lower];
164
+ m.prsApproved = prApprovedByAgent[lower] || 0;
165
+ m.prsRejected = prRejectedByAgent[lower] || 0;
166
+ }
167
+ if (runtimeByAgent[agentId]) {
168
+ // Use dispatch history as source of truth — it has full history
169
+ m.totalRuntimeMs = runtimeByAgent[agentId];
170
+ m.timedTasks = runtimeCountByAgent[agentId];
171
+ }
172
+ }
173
+
174
+ return metrics;
126
175
  }
127
176
 
128
177
  // ── Inbox ───────────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.608",
3
+ "version": "0.1.610",
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"