@yemi33/minions 0.1.330 → 0.1.332

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,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.332 (2026-04-03)
4
+
5
+ ### Fixes
6
+ - retry doesn't revert completed work items + restore skipPr guard
7
+
8
+ ## 0.1.331 (2026-04-03)
9
+
10
+ ### Fixes
11
+ - standardize PR created field to ISO format
12
+
3
13
  ## 0.1.330 (2026-04-03)
4
14
 
5
15
  ### Fixes
@@ -27,7 +27,7 @@ function prRow(pr) {
27
27
  '<td>' + (sq.reviewer && sq.status !== 'waiting' ? '<span class="pr-agent" title="' + escHtml(sq.note || '') + '">' + escHtml(sq.reviewer) + '</span>' : sq.reviewer && sq.status === 'waiting' ? '<span class="pr-agent" style="color:var(--muted)" title="Vote pending confirmation">' + escHtml(sq.reviewer) + '…</span>' : pr.reviewedBy && pr.reviewedBy.length ? '<span class="pr-agent">' + escHtml(pr.reviewedBy.join(', ')) + '</span>' : '<span style="color:var(--muted);font-size:11px">—</span>') + '</td>' +
28
28
  '<td><span class="pr-badge ' + buildClass + '">' + escHtml(buildLabel) + '</span></td>' +
29
29
  '<td><span class="pr-badge ' + statusClass + '">' + escHtml(statusLabel) + '</span></td>' +
30
- '<td><span class="pr-date">' + escHtml(pr.created || '—') + '</span></td>' +
30
+ '<td><span class="pr-date">' + escHtml((pr.created || '—').slice(0, 10)) + '</span></td>' +
31
31
  '</tr>';
32
32
  }
33
33
 
package/dashboard.js CHANGED
@@ -3458,7 +3458,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3458
3458
  branch: '',
3459
3459
  reviewStatus: autoObserve ? 'pending' : 'none',
3460
3460
  status: autoObserve ? 'active' : 'linked',
3461
- created: new Date().toISOString().slice(0, 10),
3461
+ created: new Date().toISOString(),
3462
3462
  url,
3463
3463
  prdItems: [],
3464
3464
  _manual: true,
package/engine/ado.js CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  const path = require('path');
7
7
  const shared = require('./shared');
8
- const { exec, getAdoOrgBase, addPrLink, log, dateStamp, PR_STATUS } = shared;
8
+ const { exec, getAdoOrgBase, addPrLink, log, ts, dateStamp, PR_STATUS } = shared;
9
9
  const { getPrs } = require('./queries');
10
10
 
11
11
  // Lazy require to avoid circular dependency — only needed for engine().handlePostMerge
@@ -389,7 +389,7 @@ async function reconcilePrs(config) {
389
389
  branch,
390
390
  reviewStatus: 'pending',
391
391
  status: 'active',
392
- created: (adoPr.creationDate || '').slice(0, 10) || dateStamp(),
392
+ created: adoPr.creationDate || ts(),
393
393
  url: prUrl,
394
394
  prdItems: confirmedItemId ? [confirmedItemId] : [],
395
395
  });
@@ -137,7 +137,7 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
137
137
  const items = safeJson(wiPath);
138
138
  if (!items || !Array.isArray(items)) throw new Error('work items unreadable');
139
139
  const wi = items.find(i => i.id === item.meta.item.id);
140
- if (wi && wi.status !== WI_STATUS.PAUSED) {
140
+ if (wi && wi.status !== WI_STATUS.PAUSED && wi.status !== WI_STATUS.DONE && !wi.completedAt) {
141
141
  wi._retryCount = retries + 1;
142
142
  wi.status = WI_STATUS.PENDING;
143
143
  wi._lastRetryReason = reason || '';
package/engine/github.js CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  const shared = require('./shared');
8
- const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, MINIONS_DIR, addPrLink, getPrLinks, log, dateStamp, PR_STATUS } = shared;
8
+ const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, MINIONS_DIR, addPrLink, getPrLinks, log, ts, dateStamp, PR_STATUS } = shared;
9
9
  const { getPrs } = require('./queries');
10
10
  const path = require('path');
11
11
 
@@ -373,7 +373,7 @@ async function reconcilePrs(config) {
373
373
  branch,
374
374
  reviewStatus: 'pending',
375
375
  status: 'active',
376
- created: (ghPr.created_at || '').slice(0, 10) || dateStamp(),
376
+ created: ghPr.created_at || ts(),
377
377
  url: prUrl,
378
378
  prdItems: confirmedItemId ? [confirmedItemId] : [],
379
379
  });
@@ -669,7 +669,7 @@ function syncPrsFromOutput(output, agentId, meta, config) {
669
669
  branch: meta?.branch || '',
670
670
  reviewStatus: 'pending',
671
671
  status: PR_STATUS.ACTIVE,
672
- created: dateStamp(),
672
+ created: ts(),
673
673
  url: extractPrUrl(prId),
674
674
  prdItems: meta?.item?.id ? [meta.item.id] : [],
675
675
  sourcePlan: meta?.item?.sourcePlan || '',
@@ -681,6 +681,10 @@ function syncPrsFromOutput(output, agentId, meta, config) {
681
681
  for (const [prPath, { name, entries }] of newPrsByPath) {
682
682
  mutateJsonFileLocked(prPath, (data) => {
683
683
  const prs = Array.isArray(data) ? data : [];
684
+ // Normalize legacy YYYY-MM-DD created dates to ISO
685
+ for (const p of prs) {
686
+ if (p.created && p.created.length === 10) p.created = p.created + 'T00:00:00.000Z';
687
+ }
684
688
  for (const { prId, fullId, entry } of entries) {
685
689
  if (prs.some(p => p.id === fullId || String(p.id) === String(prId))) continue;
686
690
  prs.push(entry);
@@ -1171,9 +1175,14 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
1171
1175
  const items = safeJson(wiPath) || [];
1172
1176
  const wi = items.find(i => i.id === meta.item.id);
1173
1177
  if (wi) {
1174
- wi._retryCount = retries + 1; wi.status = WI_STATUS.PENDING; delete wi.dispatched_at; delete wi.dispatched_to;
1175
- if (type === WORK_TYPE.DECOMPOSE) delete wi._decomposing; // clear so item can retry decomposition
1176
- shared.safeWrite(wiPath, items);
1178
+ // Don't revert if already completed by another code path
1179
+ if (wi.status === WI_STATUS.DONE || wi.completedAt) {
1180
+ log('info', `Skip retry for ${meta.item.id} — already completed`);
1181
+ } else {
1182
+ wi._retryCount = retries + 1; wi.status = WI_STATUS.PENDING; delete wi.dispatched_at; delete wi.dispatched_to;
1183
+ if (type === WORK_TYPE.DECOMPOSE) delete wi._decomposing;
1184
+ shared.safeWrite(wiPath, items);
1185
+ }
1177
1186
  }
1178
1187
  }
1179
1188
  } catch (err) { log('warn', `Retry update: ${err.message}`); }
@@ -1257,7 +1266,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
1257
1266
  }
1258
1267
 
1259
1268
  // Detect implement tasks that completed without creating a PR
1260
- if (isSuccess && (type === WORK_TYPE.IMPLEMENT || type === WORK_TYPE.IMPLEMENT_LARGE || type === WORK_TYPE.FIX) && prsCreatedCount === 0 && meta?.item?.id) {
1269
+ if (isSuccess && (type === WORK_TYPE.IMPLEMENT || type === WORK_TYPE.IMPLEMENT_LARGE || type === WORK_TYPE.FIX) && prsCreatedCount === 0 && meta?.item?.id && !meta?.item?.skipPr && meta?.project?.localPath) {
1261
1270
  // Check if a PR already exists linked to this work item (from a previous attempt)
1262
1271
  const projects = shared.getProjects(config);
1263
1272
  const existingPrFound = Object.values(getPrLinks()).includes(meta.item.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.330",
3
+ "version": "0.1.332",
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"