@yemi33/minions 0.1.279 → 0.1.281

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.279 (2026-04-03)
3
+ ## 0.1.281 (2026-04-03)
4
+
5
+ ### Other
6
+ - refactor: remove duplicate thinking indicator — single source via updateStreamDiv
7
+
8
+ ## 0.1.280 (2026-04-03)
9
+
10
+ ### Features
11
+ - thinking indicator shows progressive phases with elapsed timer
4
12
 
5
13
  ### Fixes
6
14
  - doc chat elapsed time no longer overlaps copy button
@@ -153,17 +153,6 @@ async function _ccDoSend(message, skipUserMsg) {
153
153
  var existingQueueEl = document.getElementById('cc-queue-indicator');
154
154
  if (existingQueueEl) existingQueueEl.remove();
155
155
 
156
- // Show thinking indicator with timer + queue count
157
- const queueCount = _ccQueue.length;
158
- const queueBadge = queueCount > 0 ? ' <span style="font-size:9px;background:var(--surface);padding:1px 5px;border-radius:8px;border:1px solid var(--border)">+' + queueCount + ' queued</span>' : '';
159
- const thinking = document.createElement('div');
160
- thinking.id = 'cc-thinking';
161
- thinking.style.cssText = 'padding:8px 12px;border-radius:8px;font-size:11px;color:var(--muted);align-self:flex-start;display:flex;align-items:center;gap:8px';
162
- thinking.innerHTML = '<span class="dot-pulse" style="display:inline-flex;gap:3px"><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.2s"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.4s"></span></span> <span id="cc-thinking-text">Thinking...</span> <span id="cc-thinking-time" style="font-size:10px;color:var(--border)"></span>' + queueBadge;
163
- document.getElementById('cc-messages').appendChild(thinking);
164
- const ccMsgs = document.getElementById('cc-messages');
165
- ccMsgs.scrollTop = ccMsgs.scrollHeight;
166
-
167
156
  const ccStartTime = Date.now();
168
157
  const phases = [
169
158
  [0, 'Thinking...'],
@@ -175,18 +164,6 @@ async function _ccDoSend(message, skipUserMsg) {
175
164
  [180000, 'Still going (this is unusually long)...'],
176
165
  [300000, 'Timing out soon...'],
177
166
  ];
178
- const ccTimer = setInterval(() => {
179
- const elapsed = Date.now() - ccStartTime;
180
- const secs = Math.floor(elapsed / 1000);
181
- const timeEl = document.getElementById('cc-thinking-time');
182
- const textEl = document.getElementById('cc-thinking-text');
183
- if (timeEl) timeEl.textContent = secs + 's';
184
- if (textEl) {
185
- for (let i = phases.length - 1; i >= 0; i--) {
186
- if (elapsed >= phases[i][0]) { textEl.textContent = phases[i][1]; break; }
187
- }
188
- }
189
- }, 500);
190
167
 
191
168
  try {
192
169
  // Stream response via SSE — shows text as it arrives
@@ -197,8 +174,6 @@ async function _ccDoSend(message, skipUserMsg) {
197
174
  });
198
175
 
199
176
  if (!res.ok) {
200
- clearInterval(ccTimer);
201
- thinking.remove();
202
177
  const errText = await res.text();
203
178
  ccAddMessage('assistant', '<span style="color:var(--red)">' + escHtml(errText || 'CC error') + '</span>' +
204
179
  (errText.includes('busy') ? ' <button onclick="ccNewSession()" style="margin-top:4px;padding:3px 10px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--blue);cursor:pointer;font-size:10px">Reset CC</button>' : ''));
@@ -206,8 +181,6 @@ async function _ccDoSend(message, skipUserMsg) {
206
181
  }
207
182
 
208
183
  // Use a temporary streaming div for live updates, then replace with ccAddMessage on completion
209
- clearInterval(ccTimer);
210
- try { thinking.remove(); } catch { /* may already be removed */ }
211
184
  // Add a temporary placeholder via ccAddMessage so it gets proper styling
212
185
  ccAddMessage('assistant', '<span style="color:var(--muted);font-size:11px">Thinking...</span>', true);
213
186
  const msgs = document.getElementById('cc-messages');
@@ -215,6 +188,15 @@ async function _ccDoSend(message, skipUserMsg) {
215
188
  let streamedText = '';
216
189
  let toolsUsed = [];
217
190
  const dotPulse = '<span style="display:inline-flex;gap:3px;margin-left:6px;vertical-align:middle"><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.2s"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.4s"></span></span>';
191
+ function _getThinkingPhase() {
192
+ var elapsed = Date.now() - ccStartTime;
193
+ var label = 'Thinking...';
194
+ for (var pi = phases.length - 1; pi >= 0; pi--) {
195
+ if (elapsed >= phases[pi][0]) { label = phases[pi][1]; break; }
196
+ }
197
+ var secs = Math.floor(elapsed / 1000);
198
+ return label + ' <span style="font-size:9px;color:var(--border)">' + secs + 's</span>';
199
+ }
218
200
  function updateStreamDiv() {
219
201
  var html = '';
220
202
  if (toolsUsed.length > 0) {
@@ -227,13 +209,16 @@ async function _ccDoSend(message, skipUserMsg) {
227
209
  if (streamedText) {
228
210
  html += renderMd(streamedText);
229
211
  }
230
- html += '<div style="margin-top:' + (streamedText ? '6px' : '0') + '"><span style="color:var(--muted);font-size:11px">Thinking...' + dotPulse + '</span></div>';
212
+ html += '<div style="margin-top:' + (streamedText ? '6px' : '0') + '"><span style="color:var(--muted);font-size:11px">' + _getThinkingPhase() + dotPulse + '</span></div>';
231
213
  streamDiv.innerHTML = html;
232
214
  // Re-append queue indicators so they stay below the streaming content
233
215
  if (_ccQueue.length > 0) _renderQueueIndicator();
234
216
  if (msgs.scrollHeight - msgs.scrollTop - msgs.clientHeight < 150) msgs.scrollTop = msgs.scrollHeight;
235
217
  }
236
218
 
219
+ // Periodically update thinking phase text + timer
220
+ const phaseTimer = setInterval(updateStreamDiv, 1000);
221
+
237
222
  const reader = res.body.getReader();
238
223
  const decoder = new TextDecoder();
239
224
  let buf = '';
@@ -257,7 +242,7 @@ async function _ccDoSend(message, skipUserMsg) {
257
242
  if (msgs.scrollHeight - msgs.scrollTop - msgs.clientHeight < 150) msgs.scrollTop = msgs.scrollHeight;
258
243
  } else if (evt.type === 'done') {
259
244
  // Replace streaming div with a proper ccAddMessage
260
- streamDiv.remove();
245
+ clearInterval(phaseTimer); streamDiv.remove();
261
246
  // placeholder was added with skipSave=true — nothing to pop
262
247
  const ccElapsed = Math.round((Date.now() - ccStartTime) / 1000);
263
248
  const rendered = renderMd(evt.text || streamedText || '');
@@ -267,7 +252,7 @@ async function _ccDoSend(message, skipUserMsg) {
267
252
  for (const action of evt.actions) { await ccExecuteAction(action); }
268
253
  }
269
254
  } else if (evt.type === 'error') {
270
- streamDiv.remove();
255
+ clearInterval(phaseTimer); streamDiv.remove();
271
256
  // placeholder was skipSave — no pop needed
272
257
  ccAddMessage('assistant', '<span style="color:var(--red)">' + escHtml(evt.error) + '</span>');
273
258
  }
@@ -281,7 +266,7 @@ async function _ccDoSend(message, skipUserMsg) {
281
266
  try {
282
267
  const evt = JSON.parse(line.slice(6));
283
268
  if (evt.type === 'done') {
284
- streamDiv.remove();
269
+ clearInterval(phaseTimer); streamDiv.remove();
285
270
  // placeholder was skipSave — no pop needed
286
271
  const ccElapsed = Math.round((Date.now() - ccStartTime) / 1000);
287
272
  const rendered = renderMd(evt.text || streamedText || '');
@@ -299,21 +284,20 @@ async function _ccDoSend(message, skipUserMsg) {
299
284
  }
300
285
  // If stream ended without a 'done' event, finalize with whatever we have
301
286
  if (streamDiv.parentNode) {
302
- streamDiv.remove();
287
+ clearInterval(phaseTimer); streamDiv.remove();
303
288
  if (streamedText) {
304
289
  const ccElapsed = Math.round((Date.now() - ccStartTime) / 1000);
305
290
  ccAddMessage('assistant', renderMd(streamedText) + '<div style="font-size:9px;color:var(--muted);margin-top:6px;display:flex;justify-content:flex-end;padding-right:30px">' + ccElapsed + 's</div>');
306
291
  }
307
292
  }
308
293
  } catch (e) {
309
- clearInterval(ccTimer);
310
- try { thinking.remove(); } catch { /* may already be removed */ }
311
294
  const retryId = 'cc-retry-' + Date.now();
312
295
  ccAddMessage('assistant', '<span style="color:var(--red)">Error: ' + escHtml(e.message) + '</span>' +
313
296
  '<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>');
314
297
  } finally {
315
298
  _ccSending = false;
316
299
  _ccAbortController = null;
300
+ try { clearInterval(phaseTimer); } catch { /* may not be defined if error before reader */ }
317
301
  try { localStorage.removeItem('cc-sending'); } catch {}
318
302
  // Show notification badge on CC button if drawer is closed
319
303
  if (!_ccOpen) showNotifBadge(document.getElementById('cc-toggle-btn'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.279",
3
+ "version": "0.1.281",
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"