@yemi33/minions 0.1.911 → 0.1.913
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 +3 -1
- package/dashboard/js/command-center.js +13 -11
- package/engine.js +40 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.913 (2026-04-13)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
|
+
- fix dep re-merge failure on retry with existing commits (#977)
|
|
6
7
|
- audit and harden log buffering implementation (#971)
|
|
7
8
|
- replace magic string 'active' with PR_STATUS.ACTIVE in lifecycle.js (#969)
|
|
8
9
|
- fix dashboard plan-pause nested lock violation (#968)
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
- add red dot notification on CC tab when response completes (#934) (#946)
|
|
17
18
|
|
|
18
19
|
### Fixes
|
|
20
|
+
- CC queued messages sent to wrong tab after tab switch
|
|
19
21
|
- pass sysPromptPath instead of steerPromptPath as system prompt in steering resume (#962)
|
|
20
22
|
- PRD item display dispatched/in-progress status conflation (closes #950) (#955)
|
|
21
23
|
- remove KB watchdog — checkpoint never written, restore never worked
|
|
@@ -368,6 +368,7 @@ async function ccSend() {
|
|
|
368
368
|
input.value = '';
|
|
369
369
|
|
|
370
370
|
var tab = _ccActiveTab();
|
|
371
|
+
var originTabId = _ccActiveTabId;
|
|
371
372
|
if (!tab) return;
|
|
372
373
|
if (!tab._queue) tab._queue = [];
|
|
373
374
|
|
|
@@ -377,16 +378,16 @@ async function ccSend() {
|
|
|
377
378
|
_renderQueueIndicator();
|
|
378
379
|
return;
|
|
379
380
|
}
|
|
380
|
-
var wasAborted = await _ccDoSend(message);
|
|
381
|
+
var wasAborted = await _ccDoSend(message, false, originTabId);
|
|
381
382
|
|
|
382
|
-
// Flush queued messages
|
|
383
|
+
// Flush queued messages to the ORIGINAL tab, even if user switched tabs
|
|
383
384
|
while (tab._queue && tab._queue.length > 0) {
|
|
384
385
|
if (wasAborted) {
|
|
385
386
|
await new Promise(function(r) { setTimeout(r, 1500); });
|
|
386
387
|
}
|
|
387
388
|
var next = tab._queue.shift();
|
|
388
389
|
_renderQueueIndicator();
|
|
389
|
-
wasAborted = await _ccDoSend(next);
|
|
390
|
+
wasAborted = await _ccDoSend(next, false, originTabId);
|
|
390
391
|
}
|
|
391
392
|
}
|
|
392
393
|
|
|
@@ -407,29 +408,30 @@ function _renderQueueIndicator() {
|
|
|
407
408
|
if (msgs.scrollHeight - msgs.scrollTop - msgs.clientHeight < 150) msgs.scrollTop = msgs.scrollHeight;
|
|
408
409
|
}
|
|
409
410
|
|
|
410
|
-
async function _ccDoSend(message, skipUserMsg) {
|
|
411
|
+
async function _ccDoSend(message, skipUserMsg, forceTabId) {
|
|
411
412
|
// Client-side /pin and /unpin — no LLM round-trip needed
|
|
412
413
|
var pinMatch = message.match(/^\/(pin|unpin)\s+(.+)/i);
|
|
413
414
|
if (pinMatch) {
|
|
414
|
-
if (!skipUserMsg) ccAddMessage('user', escHtml(message));
|
|
415
|
+
if (!skipUserMsg) ccAddMessage('user', escHtml(message), false, forceTabId);
|
|
415
416
|
var pinAction = pinMatch[1].toLowerCase();
|
|
416
417
|
var pinQuery = pinMatch[2].toLowerCase().trim();
|
|
417
418
|
var found = _ccFindPinTarget(pinQuery);
|
|
418
419
|
if (found) {
|
|
419
420
|
var wasPinned = isPinned(found.key);
|
|
420
|
-
if (pinAction === 'pin' && !wasPinned) { togglePin(found.key); ccAddMessage('assistant', 'Pinned <strong>' + escHtml(found.label) + '</strong> to top'); }
|
|
421
|
-
else if (pinAction === 'unpin' && wasPinned) { togglePin(found.key); ccAddMessage('assistant', 'Unpinned <strong>' + escHtml(found.label) + '</strong>'); }
|
|
422
|
-
else { ccAddMessage('assistant', '<strong>' + escHtml(found.label) + '</strong> is already ' + (wasPinned ? 'pinned' : 'unpinned')); }
|
|
421
|
+
if (pinAction === 'pin' && !wasPinned) { togglePin(found.key); ccAddMessage('assistant', 'Pinned <strong>' + escHtml(found.label) + '</strong> to top', false, forceTabId); }
|
|
422
|
+
else if (pinAction === 'unpin' && wasPinned) { togglePin(found.key); ccAddMessage('assistant', 'Unpinned <strong>' + escHtml(found.label) + '</strong>', false, forceTabId); }
|
|
423
|
+
else { ccAddMessage('assistant', '<strong>' + escHtml(found.label) + '</strong> is already ' + (wasPinned ? 'pinned' : 'unpinned'), false, forceTabId); }
|
|
423
424
|
showToast('cmd-toast', pinAction === 'pin' ? 'Pinned to top' : 'Unpinned', true);
|
|
424
425
|
renderInbox(inboxData); renderKnowledgeBase();
|
|
425
426
|
} else {
|
|
426
|
-
ccAddMessage('assistant', 'No inbox or KB item matching "' + escHtml(pinQuery) + '"');
|
|
427
|
+
ccAddMessage('assistant', 'No inbox or KB item matching "' + escHtml(pinQuery) + '"', false, forceTabId);
|
|
427
428
|
}
|
|
428
429
|
return;
|
|
429
430
|
}
|
|
430
431
|
|
|
431
|
-
|
|
432
|
-
var activeTabId = _ccActiveTabId;
|
|
432
|
+
// Use forced tab ID (from queue flush) or fall back to current active tab
|
|
433
|
+
var activeTabId = forceTabId || _ccActiveTabId;
|
|
434
|
+
var activeTab = _ccTabs.find(function(t) { return t.id === activeTabId; }) || _ccActiveTab();
|
|
433
435
|
if (!activeTab) return;
|
|
434
436
|
activeTab._sending = true;
|
|
435
437
|
activeTab._sendStartedAt = Date.now();
|
package/engine.js
CHANGED
|
@@ -524,7 +524,37 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
524
524
|
}
|
|
525
525
|
// Merge successfully-fetched + recovered (local-only pushed) branches sequentially
|
|
526
526
|
const fetched = fetchable.filter((_, i) => fetchResults[i].status === 'fulfilled' || recoveredBranches.has(fetchable[i].branch));
|
|
527
|
-
if (
|
|
527
|
+
// Skip dep re-merge if worktree HEAD already contains all dep commits (#973)
|
|
528
|
+
let skipDepMerge = false;
|
|
529
|
+
if (!depMergeFailed && fetched.length > 0) {
|
|
530
|
+
const ancestorChecks = await Promise.all(
|
|
531
|
+
fetched.map(async ({ branch: depBranch }) => {
|
|
532
|
+
try {
|
|
533
|
+
await execAsync(`git merge-base --is-ancestor "origin/${depBranch}" HEAD`, { ..._gitOpts, cwd: worktreePath });
|
|
534
|
+
return true;
|
|
535
|
+
} catch (_) { return false; }
|
|
536
|
+
})
|
|
537
|
+
);
|
|
538
|
+
if (ancestorChecks.every(Boolean)) {
|
|
539
|
+
log('info', `All ${fetched.length} dep branch(es) already merged into ${branchName} — skipping dep re-merge`);
|
|
540
|
+
skipDepMerge = true;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
// Stash uncommitted changes before dep merge if worktree is dirty (#973)
|
|
544
|
+
let stashed = false;
|
|
545
|
+
if (!depMergeFailed && !skipDepMerge && fetched.length > 0) {
|
|
546
|
+
try {
|
|
547
|
+
const statusOut = (await execAsync('git status --porcelain', { ..._gitOpts, cwd: worktreePath })).stdout.toString().trim();
|
|
548
|
+
if (statusOut) {
|
|
549
|
+
await execAsync('git stash push --include-untracked -m "engine: stash before dep re-merge"', { ..._gitOpts, cwd: worktreePath });
|
|
550
|
+
stashed = true;
|
|
551
|
+
log('info', `Stashed uncommitted changes in ${branchName} before dep merge`);
|
|
552
|
+
}
|
|
553
|
+
} catch (stashErr) {
|
|
554
|
+
log('warn', `Failed to stash changes in ${branchName} before dep merge: ${stashErr.message}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (!depMergeFailed && !skipDepMerge) {
|
|
528
558
|
for (const { branch: depBranch, prId } of fetched) {
|
|
529
559
|
try {
|
|
530
560
|
await execAsync(`git merge "origin/${depBranch}" --no-edit`, { ..._gitOpts, cwd: worktreePath });
|
|
@@ -573,6 +603,15 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
573
603
|
}
|
|
574
604
|
}
|
|
575
605
|
}
|
|
606
|
+
// Restore stashed changes after dep merge (#973)
|
|
607
|
+
if (stashed) {
|
|
608
|
+
try {
|
|
609
|
+
await execAsync('git stash pop', { ..._gitOpts, cwd: worktreePath });
|
|
610
|
+
log('info', `Restored stashed changes in ${branchName} after dep merge`);
|
|
611
|
+
} catch (popErr) {
|
|
612
|
+
log('warn', `git stash pop failed in ${branchName}: ${popErr.message} — stash preserved for agent`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
576
615
|
if (depMergeFailed) {
|
|
577
616
|
_cleanupPromptFiles();
|
|
578
617
|
// Build actionable failReason identifying the conflicting branch and files
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.913",
|
|
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"
|