@yemi33/minions 0.1.303 → 0.1.305

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.305 (2026-04-03)
4
+
5
+ ### Fixes
6
+ - normalize acceptanceCriteria to array (string from doc-chat crashed tick)
7
+
8
+ ## 0.1.304 (2026-04-03)
9
+
10
+ ### Features
11
+ - add Stop button to CC and doc-chat to abort in-flight requests
12
+
3
13
  ## 0.1.303 (2026-04-03)
4
14
 
5
15
  ### Fixes
@@ -9,6 +9,13 @@ let _ccAbortController = null;
9
9
  // Clear stale sending state on page load — SSE streams don't survive refresh
10
10
  try { localStorage.removeItem('cc-sending'); } catch {}
11
11
 
12
+ function ccAbort() {
13
+ if (_ccAbortController) {
14
+ _ccAbortController.abort();
15
+ _ccAbortController = null;
16
+ }
17
+ }
18
+
12
19
  function toggleCommandCenter() {
13
20
  _ccOpen = !_ccOpen;
14
21
  const drawer = document.getElementById('cc-drawer');
@@ -181,7 +188,8 @@ async function _ccDoSend(message, skipUserMsg) {
181
188
  if (elapsed >= phases[pi][0]) { label = phases[pi][1]; break; }
182
189
  }
183
190
  var secs = Math.floor(elapsed / 1000);
184
- return '<div style="display:flex;align-items:center;gap:6px"><span style="color:var(--muted);font-size:11px">' + label + '</span>' + dotPulse + '<span style="margin-left:auto;font-size:10px;color:var(--muted)">' + secs + 's</span></div>';
191
+ return '<div style="display:flex;align-items:center;gap:6px"><span style="color:var(--muted);font-size:11px">' + label + '</span>' + dotPulse + '<span style="margin-left:auto;font-size:10px;color:var(--muted)">' + secs + 's</span>' +
192
+ '<button onclick="ccAbort()" style="font-size:9px;padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer;margin-left:4px">Stop</button></div>';
185
193
  }
186
194
  function updateStreamDiv() {
187
195
  var html = '';
@@ -293,9 +301,19 @@ async function _ccDoSend(message, skipUserMsg) {
293
301
  }
294
302
  }
295
303
  } catch (e) {
296
- const retryId = 'cc-retry-' + Date.now();
297
- ccAddMessage('assistant', '<span style="color:var(--red)">Error: ' + escHtml(e.message) + '</span>' +
298
- '<button id="' + retryId + '" onclick="ccRetryLast()" style="margin-top:6px;padding:4px 12px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--blue);cursor:pointer;font-size:11px">Retry</button>');
304
+ if (streamDiv?.parentNode) { clearInterval(phaseTimer); streamDiv.remove(); }
305
+ if (e.name === 'AbortError') {
306
+ if (streamedText) {
307
+ const ccElapsed = Math.round((Date.now() - ccStartTime) / 1000);
308
+ ccAddMessage('assistant', renderMd(streamedText) + '<div style="font-size:9px;color:var(--muted);margin-top:6px">Stopped after ' + ccElapsed + 's</div>');
309
+ } else {
310
+ ccAddMessage('assistant', '<span style="color:var(--muted);font-size:11px">Stopped</span>');
311
+ }
312
+ } else {
313
+ const retryId = 'cc-retry-' + Date.now();
314
+ ccAddMessage('assistant', '<span style="color:var(--red)">Error: ' + escHtml(e.message) + '</span>' +
315
+ '<button id="' + retryId + '" onclick="ccRetryLast()" style="margin-top:6px;padding:4px 12px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--blue);cursor:pointer;font-size:11px">Retry</button>');
316
+ }
299
317
  } finally {
300
318
  _ccSending = false;
301
319
  _ccAbortController = null;
@@ -718,4 +736,4 @@ if (document.readyState === 'loading') {
718
736
  ccInitResize();
719
737
  }
720
738
 
721
- window.MinionsCC = { toggleCommandCenter, ccNewSession, ccRestoreMessages, ccSaveState, ccUpdateSessionIndicator, ccAddMessage, ccSend, ccExecuteAction };
739
+ window.MinionsCC = { toggleCommandCenter, ccNewSession, ccRestoreMessages, ccSaveState, ccUpdateSessionIndicator, ccAddMessage, ccSend, ccAbort, ccExecuteAction };
@@ -22,6 +22,7 @@ function _qaNotifySidebar(filePath) {
22
22
  }
23
23
  let _qaHistory = []; // multi-turn conversation history [{role:'user',text:''},{role:'assistant',text:''}]
24
24
  let _qaProcessing = false; // true while waiting for response
25
+ let _qaAbortController = null;
25
26
  let _qaQueue = []; // queued messages while processing
26
27
  let _qaSessionKey = ''; // key for current conversation (title or filePath)
27
28
  const _qaSessions = new Map(); // persist conversations across modal open/close {key → {history, threadHtml}}
@@ -172,10 +173,12 @@ async function _processQaMessage(message, selection) {
172
173
 
173
174
  const loadingId = 'chat-loading-' + Date.now();
174
175
  const qaQueueBadge = _qaQueue.length > 0 ? ' <span style="font-size:9px;color:var(--muted);background:var(--surface);padding:1px 5px;border-radius:8px;border:1px solid var(--border)">+' + _qaQueue.length + ' queued</span>' : '';
176
+ _qaAbortController = new AbortController();
175
177
  thread.innerHTML += '<div class="modal-qa-loading" id="' + loadingId + '">' +
176
178
  '<div class="dot-pulse"><span></span><span></span><span></span></div> ' +
177
179
  '<span id="' + loadingId + '-text">Thinking...</span> ' +
178
180
  '<span id="' + loadingId + '-time" style="font-size:10px;color:var(--muted)"></span>' +
181
+ ' <button onclick="qaAbort()" style="font-size:9px;padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer">Stop</button>' +
179
182
  qaQueueBadge + '</div>';
180
183
  thread.scrollTop = thread.scrollHeight;
181
184
 
@@ -196,6 +199,7 @@ async function _processQaMessage(message, selection) {
196
199
  const res = await fetch('/api/doc-chat', {
197
200
  method: 'POST',
198
201
  headers: { 'Content-Type': 'application/json' },
202
+ signal: _qaAbortController ? _qaAbortController.signal : undefined,
199
203
  body: JSON.stringify({
200
204
  message,
201
205
  document: capturedDocContext.content,
@@ -265,10 +269,15 @@ async function _processQaMessage(message, selection) {
265
269
  const loadingEl = document.getElementById(loadingId);
266
270
  if (loadingEl) loadingEl.remove();
267
271
  const qaElapsedExc = Math.round((Date.now() - qaStartTime) / 1000);
268
- thread.innerHTML += '<div class="modal-qa-a" style="color:var(--red)">Error: ' + escHtml(e.message) + '<div style="font-size:9px;color:var(--muted);margin-top:4px;text-align:right">' + qaElapsedExc + 's</div></div>';
272
+ if (e.name === 'AbortError') {
273
+ thread.innerHTML += '<div class="modal-qa-a" style="color:var(--muted)">Stopped<div style="font-size:9px;margin-top:4px;text-align:right">' + qaElapsedExc + 's</div></div>';
274
+ } else {
275
+ thread.innerHTML += '<div class="modal-qa-a" style="color:var(--red)">Error: ' + escHtml(e.message) + '<div style="font-size:9px;color:var(--muted);margin-top:4px;text-align:right">' + qaElapsedExc + 's</div></div>';
276
+ }
269
277
  }
270
278
 
271
279
  _qaProcessing = false;
280
+ _qaAbortController = null;
272
281
  thread.scrollTop = thread.scrollHeight;
273
282
 
274
283
  // Save session (persists even if modal was closed during processing)
@@ -305,4 +314,11 @@ async function _processQaMessage(message, selection) {
305
314
  }
306
315
  }
307
316
 
308
- window.MinionsQA = { showModalQa, modalAskAboutSelection, clearQaSelection, clearQaConversation, modalSend };
317
+ function qaAbort() {
318
+ if (_qaAbortController) {
319
+ _qaAbortController.abort();
320
+ _qaAbortController = null;
321
+ }
322
+ }
323
+
324
+ window.MinionsQA = { showModalQa, modalAskAboutSelection, clearQaSelection, clearQaConversation, modalSend, qaAbort };
package/engine.js CHANGED
@@ -1484,7 +1484,7 @@ function discoverFromWorkItems(config, project) {
1484
1484
  '- [' + (r.title || r.url) + '](' + r.url + ')' + (r.type ? ' (' + r.type + ')' : '')
1485
1485
  ).join('\n');
1486
1486
  vars.references = refs ? '## References\n\n' + refs : '';
1487
- const ac = (item.acceptanceCriteria || []).map(c => '- [ ] ' + c).join('\n');
1487
+ const ac = normalizeAc(item.acceptanceCriteria).map(c => '- [ ] ' + c).join('\n');
1488
1488
  vars.acceptance_criteria = ac ? '## Acceptance Criteria\n\n' + ac : '';
1489
1489
 
1490
1490
  // Inject checkpoint context if agent left a checkpoint.json from a prior run
@@ -1585,6 +1585,12 @@ function discoverFromWorkItems(config, project) {
1585
1585
  * Build the multi-project context section for central work items.
1586
1586
  * Inserted into the playbook via {{scope_section}}.
1587
1587
  */
1588
+ function normalizeAc(ac) {
1589
+ if (Array.isArray(ac)) return ac;
1590
+ if (typeof ac === 'string') return ac.split('\n').map(s => s.replace(/^[-*]\s*/, '').trim()).filter(Boolean);
1591
+ return [];
1592
+ }
1593
+
1588
1594
  function buildProjectContext(projects, assignedProject, isFanOut, agentName, agentRole) {
1589
1595
  const projectList = projects.map(p => {
1590
1596
  let line = `### ${p.name}\n`;
@@ -1811,7 +1817,7 @@ function discoverCentralWorkItems(config) {
1811
1817
  '- [' + (r.title || r.url) + '](' + r.url + ')' + (r.type ? ' (' + r.type + ')' : '')
1812
1818
  ).join('\n');
1813
1819
  vars.references = fanRefs ? '## References\n\n' + fanRefs : '';
1814
- const fanAc = (item.acceptanceCriteria || []).map(c => '- [ ] ' + c).join('\n');
1820
+ const fanAc = normalizeAc(item.acceptanceCriteria).map(c => '- [ ] ' + c).join('\n');
1815
1821
  vars.acceptance_criteria = fanAc ? '## Acceptance Criteria\n\n' + fanAc : '';
1816
1822
 
1817
1823
  if (workType === 'ask') {
@@ -1887,7 +1893,7 @@ function discoverCentralWorkItems(config) {
1887
1893
  '- [' + (r.title || r.url) + '](' + r.url + ')' + (r.type ? ' (' + r.type + ')' : '')
1888
1894
  ).join('\n');
1889
1895
  vars.references = normRefs ? '## References\n\n' + normRefs : '';
1890
- const normAc = (item.acceptanceCriteria || []).map(c => '- [ ] ' + c).join('\n');
1896
+ const normAc = normalizeAc(item.acceptanceCriteria).map(c => '- [ ] ' + c).join('\n');
1891
1897
  vars.acceptance_criteria = normAc ? '## Acceptance Criteria\n\n' + normAc : '';
1892
1898
 
1893
1899
  // Inject checkpoint context if agent left a checkpoint.json from a prior run
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.303",
3
+ "version": "0.1.305",
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"