@yemi33/minions 0.1.2174 → 0.1.2176
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/dashboard/slim/js/chat.js +98 -12
- package/dashboard/slim/js/command-send.js +61 -5
- package/dashboard/slim/styles.css +53 -0
- package/engine/watches.js +72 -1
- package/package.json +1 -1
|
@@ -29,6 +29,12 @@
|
|
|
29
29
|
var messages = [];
|
|
30
30
|
var sending = false;
|
|
31
31
|
var abortController = null;
|
|
32
|
+
// Per-tab queue of follow-up messages submitted while a turn is streaming
|
|
33
|
+
// (W-mqayw3x6). Keyed by tabId so each chat tab keeps its own queue across
|
|
34
|
+
// tab switches. In-memory only — a hard refresh drops the queue by design.
|
|
35
|
+
var queues = {};
|
|
36
|
+
var queueSuspended = {}; // tabId -> true after an abort/error suspends auto-drain
|
|
37
|
+
var queueEl = null; // bottom-pinned container that holds the queued bubbles
|
|
32
38
|
var currentProject = null;
|
|
33
39
|
try { currentProject = localStorage.getItem(SLIM_PROJECT_KEY) || null; } catch (_e) { /* private mode */ }
|
|
34
40
|
|
|
@@ -241,7 +247,7 @@
|
|
|
241
247
|
} else {
|
|
242
248
|
div.textContent = text;
|
|
243
249
|
}
|
|
244
|
-
|
|
250
|
+
_appendMsgEl(div);
|
|
245
251
|
scrollToBottom();
|
|
246
252
|
return div;
|
|
247
253
|
}
|
|
@@ -249,11 +255,85 @@
|
|
|
249
255
|
var div = document.createElement('div');
|
|
250
256
|
div.className = 'chat-action ' + (severity || '');
|
|
251
257
|
div.textContent = label;
|
|
252
|
-
|
|
258
|
+
_appendMsgEl(div);
|
|
253
259
|
scrollToBottom();
|
|
254
260
|
return div;
|
|
255
261
|
}
|
|
256
262
|
|
|
263
|
+
// ── Queued-message rendering (W-mqayw3x6) ──────────────────────────
|
|
264
|
+
function _getQueue() {
|
|
265
|
+
return queues[tabId] || (queues[tabId] = []);
|
|
266
|
+
}
|
|
267
|
+
// Keep a single bottom-pinned container so queued bubbles always sit below
|
|
268
|
+
// the live transcript (newest content nearest the composer). appendChild on
|
|
269
|
+
// an already-attached node moves it, so this re-pins the container to the end.
|
|
270
|
+
function _ensureQueueEl() {
|
|
271
|
+
if (!queueEl || queueEl.parentNode !== msgsEl) {
|
|
272
|
+
queueEl = document.createElement('div');
|
|
273
|
+
queueEl.className = 'chat-queue';
|
|
274
|
+
}
|
|
275
|
+
msgsEl.appendChild(queueEl);
|
|
276
|
+
return queueEl;
|
|
277
|
+
}
|
|
278
|
+
// Insert a real/stream bubble above the queue container so the pending
|
|
279
|
+
// queued bubbles stay pinned at the bottom of the transcript.
|
|
280
|
+
function _appendMsgEl(el) {
|
|
281
|
+
if (queueEl && queueEl.parentNode === msgsEl) msgsEl.insertBefore(el, queueEl);
|
|
282
|
+
else msgsEl.appendChild(el);
|
|
283
|
+
}
|
|
284
|
+
// Rebuild the queued-message bubbles for the active tab: greyed-out user
|
|
285
|
+
// bubbles with a per-message dismiss. When auto-drain is suspended (after an
|
|
286
|
+
// abort/error) a "Send queued (N)" control lets the user resume explicitly.
|
|
287
|
+
function renderQueue() {
|
|
288
|
+
var q = _getQueue();
|
|
289
|
+
if (!q.length) {
|
|
290
|
+
if (queueEl && queueEl.parentNode) queueEl.parentNode.removeChild(queueEl);
|
|
291
|
+
queueEl = null;
|
|
292
|
+
queueSuspended[tabId] = false;
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
_ensureQueueEl();
|
|
296
|
+
queueEl.textContent = '';
|
|
297
|
+
if (queueSuspended[tabId]) {
|
|
298
|
+
var resume = document.createElement('button');
|
|
299
|
+
resume.className = 'chat-queue-resume';
|
|
300
|
+
resume.type = 'button';
|
|
301
|
+
resume.textContent = 'Send queued (' + q.length + ')';
|
|
302
|
+
resume.title = 'Send the queued messages now';
|
|
303
|
+
resume.addEventListener('click', function() {
|
|
304
|
+
queueSuspended[tabId] = false;
|
|
305
|
+
renderQueue();
|
|
306
|
+
_drainQueue();
|
|
307
|
+
});
|
|
308
|
+
queueEl.appendChild(resume);
|
|
309
|
+
}
|
|
310
|
+
q.forEach(function(text, i) {
|
|
311
|
+
var row = document.createElement('div');
|
|
312
|
+
row.className = 'chat-msg user queued';
|
|
313
|
+
var span = document.createElement('span');
|
|
314
|
+
span.className = 'chat-queue-text';
|
|
315
|
+
span.textContent = text;
|
|
316
|
+
row.appendChild(span);
|
|
317
|
+
var badge = document.createElement('span');
|
|
318
|
+
badge.className = 'chat-queue-badge';
|
|
319
|
+
badge.textContent = 'queued';
|
|
320
|
+
row.appendChild(badge);
|
|
321
|
+
var dismiss = document.createElement('button');
|
|
322
|
+
dismiss.className = 'chat-queue-dismiss';
|
|
323
|
+
dismiss.type = 'button';
|
|
324
|
+
dismiss.textContent = '×';
|
|
325
|
+
dismiss.title = 'Remove from queue';
|
|
326
|
+
dismiss.addEventListener('click', function() {
|
|
327
|
+
var qq = _getQueue();
|
|
328
|
+
qq.splice(i, 1);
|
|
329
|
+
renderQueue();
|
|
330
|
+
});
|
|
331
|
+
row.appendChild(dismiss);
|
|
332
|
+
queueEl.appendChild(row);
|
|
333
|
+
});
|
|
334
|
+
scrollToBottom();
|
|
335
|
+
}
|
|
336
|
+
|
|
257
337
|
// Render a tool invocation as a one-line string. `full` keeps the complete
|
|
258
338
|
// command/args (used by the tool-calls modal); otherwise long values are
|
|
259
339
|
// clipped for the collapsed 3-line panel.
|
|
@@ -370,7 +450,7 @@
|
|
|
370
450
|
tp.panel.style.display = 'none';
|
|
371
451
|
toolbar.appendChild(tp.panel);
|
|
372
452
|
|
|
373
|
-
|
|
453
|
+
_appendMsgEl(div);
|
|
374
454
|
scrollToBottom();
|
|
375
455
|
|
|
376
456
|
return {
|
|
@@ -411,26 +491,32 @@
|
|
|
411
491
|
|
|
412
492
|
function setSending(on) {
|
|
413
493
|
sending = on;
|
|
414
|
-
|
|
415
|
-
|
|
494
|
+
// Composer stays interactive while a turn streams so the user can keep
|
|
495
|
+
// typing and queue follow-up messages (W-mqayw3x6 #1). The Send button
|
|
496
|
+
// stays enabled — submitting mid-turn enqueues — and only its label
|
|
497
|
+
// reflects the in-flight state.
|
|
498
|
+
sendBtn.disabled = false;
|
|
499
|
+
sendBtn.textContent = on ? 'Queue' : 'Send';
|
|
416
500
|
stopBtn.style.display = on ? 'block' : 'none';
|
|
417
|
-
inputEl.disabled =
|
|
501
|
+
inputEl.disabled = false;
|
|
418
502
|
}
|
|
419
503
|
|
|
420
504
|
function rerenderHistory() {
|
|
421
505
|
msgsEl.innerHTML = '';
|
|
506
|
+
queueEl = null; // detached by the innerHTML reset; renderQueue rebuilds it
|
|
422
507
|
if (!messages.length) {
|
|
423
508
|
var empty = document.createElement('div');
|
|
424
509
|
empty.className = 'chat-empty';
|
|
425
510
|
empty.textContent = 'No messages yet — say hi to Command Center.';
|
|
426
511
|
msgsEl.appendChild(empty);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
512
|
+
} else {
|
|
513
|
+
for (var i = 0; i < messages.length; i++) {
|
|
514
|
+
var m = messages[i];
|
|
515
|
+
if (m.role === 'action') appendActionStatus(m.severity || '', m.text || '');
|
|
516
|
+
else appendBubble(m.role, m.text || '', m.toolCalls);
|
|
517
|
+
}
|
|
433
518
|
}
|
|
519
|
+
renderQueue();
|
|
434
520
|
}
|
|
435
521
|
// ── Command Center tab bar (shared with the classic dashboard) ──
|
|
436
522
|
// One chip per shared cc-tabs entry, so the same conversations are visible
|
|
@@ -23,10 +23,60 @@
|
|
|
23
23
|
saveState();
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// Public entry point for the Send button / Enter key. While a turn is in
|
|
27
|
+
// flight the composer stays interactive (setSending no longer disables it),
|
|
28
|
+
// so a second submit QUEUES the message instead of dropping it or racing the
|
|
29
|
+
// active stream. The queue drains FIFO, one turn at a time (W-mqayw3x6).
|
|
30
|
+
function sendMessage() {
|
|
28
31
|
var text = inputEl.value.trim();
|
|
29
32
|
if (!text) return;
|
|
33
|
+
inputEl.value = '';
|
|
34
|
+
inputEl.style.height = 'auto';
|
|
35
|
+
if (sending) {
|
|
36
|
+
_enqueue(text);
|
|
37
|
+
inputEl.focus();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
_performSend(text);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Append to the active tab's pending queue and render the greyed bubbles.
|
|
44
|
+
function _enqueue(text) {
|
|
45
|
+
_getQueue().push(text);
|
|
46
|
+
renderQueue();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Promote the next queued message for the active tab, one at a time. No-op
|
|
50
|
+
// while a turn is in flight or the queue is suspended (after abort/error).
|
|
51
|
+
function _drainQueue() {
|
|
52
|
+
if (sending || queueSuspended[tabId]) return;
|
|
53
|
+
var q = _getQueue();
|
|
54
|
+
if (!q.length) return;
|
|
55
|
+
var next = q.shift();
|
|
56
|
+
renderQueue();
|
|
57
|
+
_performSend(next);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Called from a finished turn's `finally`. On clean completion auto-fire the
|
|
61
|
+
// next queued message; on abort or error suspend auto-drain and leave the
|
|
62
|
+
// queue visible so the user can resend or dismiss explicitly (W-mqayw3x6 #5/#6).
|
|
63
|
+
function _afterTurn(turnTabId, outcome) {
|
|
64
|
+
var q = queues[turnTabId];
|
|
65
|
+
if (!q || !q.length) return;
|
|
66
|
+
if (outcome === 'done' && turnTabId === tabId) {
|
|
67
|
+
_drainQueue();
|
|
68
|
+
} else {
|
|
69
|
+
queueSuspended[turnTabId] = true;
|
|
70
|
+
if (turnTabId === tabId) renderQueue();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function _performSend(text) {
|
|
75
|
+
// Bind this turn to the tab it started on so a mid-turn tab switch (which
|
|
76
|
+
// aborts the stream) settles the queue against the right tab, not the one
|
|
77
|
+
// the user navigated to.
|
|
78
|
+
var turnTabId = tabId;
|
|
79
|
+
var turnOutcome = 'done';
|
|
30
80
|
|
|
31
81
|
// The tab title is derived from the first user message; only that message
|
|
32
82
|
// changes the chip, so re-render the bar once instead of on every send.
|
|
@@ -36,8 +86,6 @@
|
|
|
36
86
|
saveState();
|
|
37
87
|
if (wasFirstUser) renderTabBar();
|
|
38
88
|
|
|
39
|
-
inputEl.value = '';
|
|
40
|
-
inputEl.style.height = 'auto';
|
|
41
89
|
setSending(true);
|
|
42
90
|
|
|
43
91
|
var stream = buildStreamBubble();
|
|
@@ -101,6 +149,7 @@
|
|
|
101
149
|
stream.replaceWithError(errorEvt.error || 'Error');
|
|
102
150
|
messages.push({ role: 'error', text: errorEvt.error || 'Error' });
|
|
103
151
|
saveState();
|
|
152
|
+
turnOutcome = 'error';
|
|
104
153
|
return;
|
|
105
154
|
}
|
|
106
155
|
|
|
@@ -130,6 +179,7 @@
|
|
|
130
179
|
} catch (e) {
|
|
131
180
|
if (e && (e.name === 'AbortError' || /aborted/i.test(String(e.message || '')))) {
|
|
132
181
|
userAborted = true;
|
|
182
|
+
turnOutcome = 'aborted';
|
|
133
183
|
if (streamedText) {
|
|
134
184
|
stream.finalize(streamedText);
|
|
135
185
|
messages.push({ role: 'assistant', text: streamedText, toolCalls: stream.getToolList() });
|
|
@@ -139,6 +189,7 @@
|
|
|
139
189
|
}
|
|
140
190
|
saveState();
|
|
141
191
|
} else {
|
|
192
|
+
turnOutcome = 'error';
|
|
142
193
|
var msg = 'Send failed: ' + (e && e.message ? e.message : e);
|
|
143
194
|
stream.replaceWithError(msg);
|
|
144
195
|
messages.push({ role: 'error', text: msg });
|
|
@@ -147,7 +198,8 @@
|
|
|
147
198
|
} finally {
|
|
148
199
|
setSending(false);
|
|
149
200
|
abortController = null;
|
|
150
|
-
if (!userAborted) inputEl.focus();
|
|
201
|
+
if (turnTabId === tabId && !userAborted) inputEl.focus();
|
|
202
|
+
_afterTurn(turnTabId, turnOutcome);
|
|
151
203
|
}
|
|
152
204
|
}
|
|
153
205
|
|
|
@@ -165,6 +217,10 @@
|
|
|
165
217
|
|
|
166
218
|
async function slimChatNew() {
|
|
167
219
|
if (sending) abortInFlight();
|
|
220
|
+
// New chat = fresh session: drop the leaving tab's pending queue (#7). The
|
|
221
|
+
// aborted in-flight turn's _afterTurn then finds an empty queue and no-ops.
|
|
222
|
+
delete queues[tabId];
|
|
223
|
+
queueSuspended[tabId] = false;
|
|
168
224
|
// Register a fresh tab in the shared cc-tabs store and switch slim onto
|
|
169
225
|
// it — classic's other tabs are left intact, and the previous session
|
|
170
226
|
// keeps its own server-side cc-sessions.json entry (no DELETE).
|
|
@@ -360,6 +360,59 @@
|
|
|
360
360
|
border: 1px solid var(--red);
|
|
361
361
|
font-size: var(--text-lg);
|
|
362
362
|
}
|
|
363
|
+
/* Queued messages (W-mqayw3x6): follow-ups submitted while a turn is still
|
|
364
|
+
streaming. The container is pinned to the bottom of the transcript; each
|
|
365
|
+
item is a greyed-out user bubble that fires FIFO on turn completion. */
|
|
366
|
+
.chat-queue {
|
|
367
|
+
display: flex;
|
|
368
|
+
flex-direction: column;
|
|
369
|
+
gap: 8px;
|
|
370
|
+
align-self: stretch;
|
|
371
|
+
}
|
|
372
|
+
.chat-msg.user.queued {
|
|
373
|
+
align-self: flex-end;
|
|
374
|
+
background: var(--surface2);
|
|
375
|
+
color: var(--muted);
|
|
376
|
+
border: 1px dashed var(--border);
|
|
377
|
+
opacity: 0.8;
|
|
378
|
+
display: flex;
|
|
379
|
+
align-items: center;
|
|
380
|
+
gap: 8px;
|
|
381
|
+
}
|
|
382
|
+
.chat-queue-text { white-space: pre-wrap; word-wrap: break-word; }
|
|
383
|
+
.chat-queue-badge {
|
|
384
|
+
font-size: var(--text-base);
|
|
385
|
+
text-transform: uppercase;
|
|
386
|
+
letter-spacing: 0.5px;
|
|
387
|
+
color: var(--muted);
|
|
388
|
+
border: 1px solid var(--border);
|
|
389
|
+
border-radius: var(--radius);
|
|
390
|
+
padding: 1px 6px;
|
|
391
|
+
flex-shrink: 0;
|
|
392
|
+
}
|
|
393
|
+
.chat-queue-dismiss {
|
|
394
|
+
background: none;
|
|
395
|
+
border: none;
|
|
396
|
+
color: var(--muted);
|
|
397
|
+
cursor: pointer;
|
|
398
|
+
font-size: var(--text-lg);
|
|
399
|
+
line-height: 1;
|
|
400
|
+
padding: 0 2px;
|
|
401
|
+
flex-shrink: 0;
|
|
402
|
+
}
|
|
403
|
+
.chat-queue-dismiss:hover { color: var(--red); }
|
|
404
|
+
.chat-queue-resume {
|
|
405
|
+
align-self: center;
|
|
406
|
+
background: var(--blue);
|
|
407
|
+
color: #fff;
|
|
408
|
+
border: none;
|
|
409
|
+
border-radius: var(--radius);
|
|
410
|
+
padding: 6px 14px;
|
|
411
|
+
font-size: var(--text-md);
|
|
412
|
+
font-weight: 600;
|
|
413
|
+
cursor: pointer;
|
|
414
|
+
}
|
|
415
|
+
.chat-queue-resume:hover { filter: brightness(1.1); }
|
|
363
416
|
.chat-input-wrap {
|
|
364
417
|
display: flex;
|
|
365
418
|
gap: 8px;
|
package/engine/watches.js
CHANGED
|
@@ -150,7 +150,14 @@ function registerTargetType(type, spec) {
|
|
|
150
150
|
throw new Error(`registerTargetType(${type}): absoluteConditions entry '${c}' is not in conditions[]`);
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
-
|
|
153
|
+
// W-mqa63opd000ha836 — optional hook: returns true when the target has
|
|
154
|
+
// reached a state from which `condition` can never fire again (e.g. a
|
|
155
|
+
// PR's status is `merged` and we're watching `build-fail`). Default
|
|
156
|
+
// returns false (back-compat — current target types unchanged).
|
|
157
|
+
const terminalFn = typeof spec.isTerminalForCondition === 'function'
|
|
158
|
+
? spec.isTerminalForCondition
|
|
159
|
+
: () => false;
|
|
160
|
+
TARGET_TYPES[type] = { ...spec, absoluteConditions: absoluteSet, isTerminalForCondition: terminalFn };
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
/** Returns the registered spec for a target type, or null. */
|
|
@@ -517,6 +524,40 @@ function checkWatches(config, state) {
|
|
|
517
524
|
});
|
|
518
525
|
}
|
|
519
526
|
|
|
527
|
+
// W-mqa63opd000ha836 — auto-expire watches whose target has reached a
|
|
528
|
+
// terminal state from which the watched condition can never fire again.
|
|
529
|
+
// Independent of the absolute-condition fire-once path above: covers the
|
|
530
|
+
// long-tail case where a `build-fail` (or similar) watch was armed on an
|
|
531
|
+
// active PR that subsequently merged without ever tripping its condition.
|
|
532
|
+
// Without this, the watch sits `active` forever, polling every interval.
|
|
533
|
+
//
|
|
534
|
+
// Guardrail: do NOT auto-expire until the watch has had a chance to fire
|
|
535
|
+
// on its first real check — `triggerCount > 0` covers post-fire watches;
|
|
536
|
+
// `prevState.status === entity.status` covers second-and-later checks
|
|
537
|
+
// (initial _captureState on tick-1 makes these equal, so a watch armed
|
|
538
|
+
// on an already-terminal target with `condition: merged` still fires
|
|
539
|
+
// once via the absolute-condition path before this branch expires it).
|
|
540
|
+
if (watch.status === WATCH_STATUS.ACTIVE) {
|
|
541
|
+
const _ttForTerm = TARGET_TYPES[watch.targetType];
|
|
542
|
+
if (_ttForTerm && typeof _ttForTerm.isTerminalForCondition === 'function') {
|
|
543
|
+
try {
|
|
544
|
+
const _entityForTerm = _ttForTerm.fetchEntity(watch.target, state || {});
|
|
545
|
+
if (_entityForTerm) {
|
|
546
|
+
const _hadShot = (watch.triggerCount || 0) > 0
|
|
547
|
+
|| (previousState && previousState.status !== undefined
|
|
548
|
+
&& previousState.status === _entityForTerm.status);
|
|
549
|
+
if (_hadShot && _ttForTerm.isTerminalForCondition(watch.condition, _entityForTerm, previousState)) {
|
|
550
|
+
watch.status = WATCH_STATUS.EXPIRED;
|
|
551
|
+
const _termStatus = _entityForTerm.status;
|
|
552
|
+
log('info', `Watch auto-expired (terminal target state): ${watch.id} — ${watch.targetType} ${watch.target} is ${_termStatus}, condition ${watch.condition} cannot fire again`);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
} catch (termErr) {
|
|
556
|
+
log('warn', `Watch terminal-state check error (${watch.id}): ${termErr.message}`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
520
561
|
// Capture state for change detection on next check
|
|
521
562
|
watch._lastState = _captureState(watch, state);
|
|
522
563
|
|
|
@@ -704,6 +745,36 @@ registerTargetType(WATCH_TARGET_TYPE.PR, {
|
|
|
704
745
|
WATCH_CONDITION.MERGED, WATCH_CONDITION.BUILD_FAIL, WATCH_CONDITION.BUILD_PASS,
|
|
705
746
|
WATCH_CONDITION.READY_FOR_MERGE,
|
|
706
747
|
],
|
|
748
|
+
// W-mqa63opd000ha836 — zombie-watch janitor: once a PR reaches a terminal
|
|
749
|
+
// status (merged / closed / abandoned), most PR conditions can never fire
|
|
750
|
+
// again so the watch should auto-expire instead of polling forever.
|
|
751
|
+
//
|
|
752
|
+
// Excluded from terminal-expire:
|
|
753
|
+
// - merged — already handled by the absoluteConditions fire-once
|
|
754
|
+
// path; including it here is redundant and risks
|
|
755
|
+
// expiring before the watch's one shot to fire.
|
|
756
|
+
// - status-change — could theoretically still fire on a status flip
|
|
757
|
+
// (e.g. closed→reopened on GitHub), even though rare.
|
|
758
|
+
// - any — same rationale as status-change.
|
|
759
|
+
//
|
|
760
|
+
// Build-fail / build-pass / vote-change / new-comments / head-commit-change /
|
|
761
|
+
// mergeable-flipped / behind-master / ready-for-merge / draft-flipped all
|
|
762
|
+
// mutate fields that are frozen the moment the PR is merged/closed/abandoned,
|
|
763
|
+
// so a fresh fire is impossible.
|
|
764
|
+
isTerminalForCondition: (condition, pr) => {
|
|
765
|
+
if (!pr || !pr.status) return false;
|
|
766
|
+
const status = pr.status;
|
|
767
|
+
const isTerminal = status === shared.PR_STATUS.MERGED
|
|
768
|
+
|| status === shared.PR_STATUS.CLOSED
|
|
769
|
+
|| status === shared.PR_STATUS.ABANDONED;
|
|
770
|
+
if (!isTerminal) return false;
|
|
771
|
+
if (condition === WATCH_CONDITION.MERGED
|
|
772
|
+
|| condition === WATCH_CONDITION.STATUS_CHANGE
|
|
773
|
+
|| condition === WATCH_CONDITION.ANY) {
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
return true;
|
|
777
|
+
},
|
|
707
778
|
fetchEntity: (target, state) => findPrByTarget(state.pullRequests, target),
|
|
708
779
|
captureState: (pr) => ({
|
|
709
780
|
status: pr.status, buildStatus: pr.buildStatus, reviewStatus: pr.reviewStatus,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2176",
|
|
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"
|