@yemi33/minions 0.1.367 → 0.1.369

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.369 (2026-04-06)
4
+
5
+ ### Fixes
6
+ - copy button preserves markdown formatting
7
+
8
+ ## 0.1.368 (2026-04-06)
9
+
10
+ ### Fixes
11
+ - dashboard audit pass 2 — CC error handling, charter edits, archived PRDs (#214)
12
+
3
13
  ## 0.1.367 (2026-04-06)
4
14
 
5
15
  ### Fixes
@@ -348,6 +348,12 @@ function ccRetryLast() {
348
348
  _ccDoSend(text.trim());
349
349
  }
350
350
 
351
+ async function _ccFetch(url, body) {
352
+ const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
353
+ if (!res.ok) { const d = await res.json().catch(() => ({})); throw new Error(d.error || 'Request failed (' + res.status + ')'); }
354
+ return res;
355
+ }
356
+
351
357
  async function ccExecuteAction(action) {
352
358
  const msgs = document.getElementById('cc-messages');
353
359
  const status = document.createElement('div');
@@ -356,13 +362,10 @@ async function ccExecuteAction(action) {
356
362
  try {
357
363
  switch (action.type) {
358
364
  case 'dispatch': {
359
- const res = await fetch('/api/work-items', {
360
- method: 'POST', headers: { 'Content-Type': 'application/json' },
361
- body: JSON.stringify({
365
+ const res = await _ccFetch('/api/work-items', {
362
366
  title: action.title, type: action.workType || 'implement',
363
367
  priority: action.priority || 'medium', description: action.description || '',
364
368
  project: action.project || '', agents: action.agents || [],
365
- })
366
369
  });
367
370
  const d = await res.json();
368
371
  status.innerHTML = '&#10003; Dispatched: <strong>' + escHtml(d.id || action.title) + '</strong>';
@@ -371,49 +374,33 @@ async function ccExecuteAction(action) {
371
374
  break;
372
375
  }
373
376
  case 'note': {
374
- const today = new Date().toISOString().slice(0, 10);
375
- await fetch('/api/notes', {
376
- method: 'POST', headers: { 'Content-Type': 'application/json' },
377
- body: JSON.stringify({ title: action.title, what: action.content || action.description, author: 'command-center' })
378
- });
377
+ await _ccFetch('/api/notes', { title: action.title, what: action.content || action.description, author: 'command-center' });
379
378
  status.innerHTML = '&#10003; Note saved: <strong>' + escHtml(action.title) + '</strong>';
380
379
  status.style.color = 'var(--green)';
381
380
  break;
382
381
  }
383
382
  case 'pin': {
384
- await fetch('/api/pinned', {
385
- method: 'POST', headers: { 'Content-Type': 'application/json' },
386
- body: JSON.stringify({ title: action.title, content: action.content || action.description, level: action.level || '' })
387
- });
383
+ await _ccFetch('/api/pinned', { title: action.title, content: action.content || action.description, level: action.level || '' });
388
384
  status.innerHTML = '&#x1F4CC; Pinned: <strong>' + escHtml(action.title) + '</strong> — visible to all agents';
389
385
  status.style.color = 'var(--green)';
390
386
  refresh();
391
387
  break;
392
388
  }
393
389
  case 'plan': {
394
- await fetch('/api/plan', {
395
- method: 'POST', headers: { 'Content-Type': 'application/json' },
396
- body: JSON.stringify({ title: action.title, description: action.description, project: action.project, branchStrategy: action.branchStrategy || 'parallel' })
397
- });
390
+ await _ccFetch('/api/plan', { title: action.title, description: action.description, project: action.project, branchStrategy: action.branchStrategy || 'parallel' });
398
391
  status.innerHTML = '&#10003; Plan queued: <strong>' + escHtml(action.title) + '</strong>';
399
392
  status.style.color = 'var(--green)';
400
393
  break;
401
394
  }
402
395
  case 'cancel': {
403
- await fetch('/api/agents/cancel', {
404
- method: 'POST', headers: { 'Content-Type': 'application/json' },
405
- body: JSON.stringify({ agentId: action.agent, reason: action.reason || 'Cancelled via command center' })
406
- });
396
+ await _ccFetch('/api/agents/cancel', { agentId: action.agent, reason: action.reason || 'Cancelled via command center' });
407
397
  status.innerHTML = '&#10003; Cancelled agent: <strong>' + escHtml(action.agent) + '</strong>';
408
398
  status.style.color = 'var(--orange)';
409
399
  break;
410
400
  }
411
401
  case 'retry': {
412
402
  for (const id of (action.ids || [])) {
413
- await fetch('/api/work-items/retry', {
414
- method: 'POST', headers: { 'Content-Type': 'application/json' },
415
- body: JSON.stringify({ id, source: '' })
416
- });
403
+ await _ccFetch('/api/work-items/retry', { id, source: '' });
417
404
  }
418
405
  status.innerHTML = '&#10003; Retried: <strong>' + escHtml((action.ids || []).join(', ')) + '</strong>';
419
406
  status.style.color = 'var(--green)';
@@ -421,46 +408,31 @@ async function ccExecuteAction(action) {
421
408
  break;
422
409
  }
423
410
  case 'pause-plan': {
424
- await fetch('/api/plans/pause', {
425
- method: 'POST', headers: { 'Content-Type': 'application/json' },
426
- body: JSON.stringify({ file: action.file })
427
- });
411
+ await _ccFetch('/api/plans/pause', { file: action.file });
428
412
  status.innerHTML = '&#10003; Paused plan: <strong>' + escHtml(action.file) + '</strong>';
429
413
  status.style.color = 'var(--orange)';
430
414
  break;
431
415
  }
432
416
  case 'approve-plan': {
433
- await fetch('/api/plans/approve', {
434
- method: 'POST', headers: { 'Content-Type': 'application/json' },
435
- body: JSON.stringify({ file: action.file })
436
- });
417
+ await _ccFetch('/api/plans/approve', { file: action.file });
437
418
  status.innerHTML = '&#10003; Approved plan: <strong>' + escHtml(action.file) + '</strong>';
438
419
  status.style.color = 'var(--green)';
439
420
  break;
440
421
  }
441
422
  case 'edit-prd-item': {
442
- await fetch('/api/prd-items/update', {
443
- method: 'POST', headers: { 'Content-Type': 'application/json' },
444
- body: JSON.stringify({ source: action.source, itemId: action.itemId, name: action.name, description: action.description, priority: action.priority, estimated_complexity: action.complexity })
445
- });
423
+ await _ccFetch('/api/prd-items/update', { source: action.source, itemId: action.itemId, name: action.name, description: action.description, priority: action.priority, estimated_complexity: action.complexity });
446
424
  status.innerHTML = '&#10003; Updated PRD item: <strong>' + escHtml(action.itemId) + '</strong>';
447
425
  status.style.color = 'var(--green)';
448
426
  break;
449
427
  }
450
428
  case 'remove-prd-item': {
451
- await fetch('/api/prd-items/remove', {
452
- method: 'POST', headers: { 'Content-Type': 'application/json' },
453
- body: JSON.stringify({ source: action.source, itemId: action.itemId })
454
- });
429
+ await _ccFetch('/api/prd-items/remove', { source: action.source, itemId: action.itemId });
455
430
  status.innerHTML = '&#10003; Removed PRD item: <strong>' + escHtml(action.itemId) + '</strong>';
456
431
  status.style.color = 'var(--orange)';
457
432
  break;
458
433
  }
459
434
  case 'delete-work-item': {
460
- await fetch('/api/work-items/delete', {
461
- method: 'POST', headers: { 'Content-Type': 'application/json' },
462
- body: JSON.stringify({ id: action.id, source: action.source || '' })
463
- });
435
+ await _ccFetch('/api/work-items/delete', { id: action.id, source: action.source || '' });
464
436
  status.innerHTML = '&#10003; Deleted work item: <strong>' + escHtml(action.id) + '</strong>';
465
437
  status.style.color = 'var(--orange)';
466
438
  break;
@@ -585,48 +557,33 @@ async function ccExecuteAction(action) {
585
557
  break;
586
558
  }
587
559
  case 'unpin': {
588
- await fetch('/api/pinned/remove', {
589
- method: 'POST', headers: { 'Content-Type': 'application/json' },
590
- body: JSON.stringify({ title: action.title })
591
- });
560
+ await _ccFetch('/api/pinned/remove', { title: action.title });
592
561
  status.innerHTML = '&#10003; Unpinned: <strong>' + escHtml(action.title) + '</strong>';
593
562
  status.style.color = 'var(--green)';
594
563
  refresh();
595
564
  break;
596
565
  }
597
566
  case 'archive-plan': {
598
- await fetch('/api/plans/archive', {
599
- method: 'POST', headers: { 'Content-Type': 'application/json' },
600
- body: JSON.stringify({ file: action.file })
601
- });
567
+ await _ccFetch('/api/plans/archive', { file: action.file });
602
568
  status.innerHTML = '&#10003; Archived plan: <strong>' + escHtml(action.file) + '</strong>';
603
569
  status.style.color = 'var(--green)';
604
570
  refresh();
605
571
  break;
606
572
  }
607
573
  case 'reject-plan': {
608
- await fetch('/api/plans/reject', {
609
- method: 'POST', headers: { 'Content-Type': 'application/json' },
610
- body: JSON.stringify({ file: action.file, reason: action.reason || '' })
611
- });
574
+ await _ccFetch('/api/plans/reject', { file: action.file, reason: action.reason || '' });
612
575
  status.innerHTML = '&#10003; Rejected plan: <strong>' + escHtml(action.file) + '</strong>';
613
576
  status.style.color = 'var(--orange)';
614
577
  break;
615
578
  }
616
579
  case 'steer-agent': {
617
- await fetch('/api/agents/steer', {
618
- method: 'POST', headers: { 'Content-Type': 'application/json' },
619
- body: JSON.stringify({ agent: action.agent, message: action.message || action.content })
620
- });
580
+ await _ccFetch('/api/agents/steer', { agent: action.agent, message: action.message || action.content });
621
581
  status.innerHTML = '&#10003; Steering message sent to <strong>' + escHtml(action.agent) + '</strong>';
622
582
  status.style.color = 'var(--green)';
623
583
  break;
624
584
  }
625
585
  case 'add-meeting-note': {
626
- await fetch('/api/meetings/note', {
627
- method: 'POST', headers: { 'Content-Type': 'application/json' },
628
- body: JSON.stringify({ id: action.id, note: action.note || action.content })
629
- });
586
+ await _ccFetch('/api/meetings/note', { id: action.id, note: action.note || action.content });
630
587
  status.innerHTML = '&#10003; Note added to meeting <strong>' + escHtml(action.id) + '</strong>';
631
588
  status.style.color = 'var(--green)';
632
589
  break;
@@ -643,30 +600,21 @@ async function ccExecuteAction(action) {
643
600
  break;
644
601
  }
645
602
  case 'link-pr': {
646
- await fetch('/api/pull-requests/link', {
647
- method: 'POST', headers: { 'Content-Type': 'application/json' },
648
- body: JSON.stringify({ url: action.url, title: action.title || '', project: action.project || '', autoObserve: action.autoObserve !== false })
649
- });
603
+ await _ccFetch('/api/pull-requests/link', { url: action.url, title: action.title || '', project: action.project || '', autoObserve: action.autoObserve !== false });
650
604
  status.innerHTML = '&#10003; PR linked: <strong>' + escHtml(action.url) + '</strong>';
651
605
  status.style.color = 'var(--green)';
652
606
  refresh();
653
607
  break;
654
608
  }
655
609
  case 'archive-meeting': {
656
- await fetch('/api/meetings/archive', {
657
- method: 'POST', headers: { 'Content-Type': 'application/json' },
658
- body: JSON.stringify({ id: action.id })
659
- });
610
+ await _ccFetch('/api/meetings/archive', { id: action.id });
660
611
  status.innerHTML = '&#10003; Meeting archived: <strong>' + escHtml(action.id) + '</strong>';
661
612
  status.style.color = 'var(--green)';
662
613
  refresh();
663
614
  break;
664
615
  }
665
616
  case 'update-routing': {
666
- await fetch('/api/settings/routing', {
667
- method: 'POST', headers: { 'Content-Type': 'application/json' },
668
- body: JSON.stringify({ content: action.content })
669
- });
617
+ await _ccFetch('/api/settings/routing', { content: action.content });
670
618
  status.innerHTML = '&#10003; Routing updated';
671
619
  status.style.color = 'var(--green)';
672
620
  break;
@@ -1,5 +1,7 @@
1
1
  // dashboard/js/detail-panel.js — Agent detail panel extracted from dashboard.html
2
2
 
3
+ let _charterRawCache = ''; // stored outside DOM to survive innerHTML rewrites on tab switch
4
+
3
5
  function closeDetail() {
4
6
  document.getElementById('detail-overlay').classList.remove('open');
5
7
  document.getElementById('detail-panel').classList.remove('open');
@@ -88,7 +90,7 @@ function renderDetailContent(detail, tab) {
88
90
  '</div>' +
89
91
  '<div id="charter-view" class="section">' + renderMd(charterContent || 'No charter found. Click Edit to create one.') + '</div>' +
90
92
  '<textarea id="charter-editor" style="display:none;width:100%;min-height:300px;padding:8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-family:Consolas,monospace;font-size:12px;resize:vertical">' + escHtml(charterContent) + '</textarea>';
91
- el._charterRaw = charterContent;
93
+ _charterRawCache = charterContent;
92
94
  } else if (tab === 'history') {
93
95
  let html = '';
94
96
  // Recent dispatch results
@@ -126,7 +128,7 @@ function _toggleCharterEdit() {
126
128
 
127
129
  function _cancelCharterEdit() {
128
130
  const el = document.getElementById('detail-content');
129
- document.getElementById('charter-editor').value = el._charterRaw || '';
131
+ document.getElementById('charter-editor').value = _charterRawCache || '';
130
132
  document.getElementById('charter-view').style.display = '';
131
133
  document.getElementById('charter-editor').style.display = 'none';
132
134
  document.getElementById('charter-edit-btn').style.display = '';
@@ -146,7 +148,7 @@ async function _saveCharter() {
146
148
  });
147
149
  if (res.ok) {
148
150
  document.getElementById('charter-view').innerHTML = renderMd(content);
149
- document.getElementById('detail-content')._charterRaw = content;
151
+ _charterRawCache = content;
150
152
  _cancelCharterEdit();
151
153
  showToast('cmd-toast', 'Charter saved', true);
152
154
  } else {
@@ -49,7 +49,9 @@ function closeModal() {
49
49
  function copyModalContent() {
50
50
  const body = document.getElementById('modal-body');
51
51
  const btn = document.getElementById('modal-copy-btn');
52
- navigator.clipboard.writeText(body.textContent).then(() => {
52
+ // Prefer raw markdown source if available, fall back to rendered text
53
+ const text = (typeof _modalDocContext !== 'undefined' && _modalDocContext.content) ? _modalDocContext.content : body.textContent;
54
+ navigator.clipboard.writeText(text).then(() => {
53
55
  btn.classList.add('copied');
54
56
  btn.innerHTML = '<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"/></svg> Copied';
55
57
  setTimeout(() => {
@@ -62,7 +62,7 @@ function renderKnowledgeBase() {
62
62
  if (!Array.isArray(catItems)) continue;
63
63
  for (const item of catItems) items.push({ ...item, category: cat });
64
64
  }
65
- items.sort((a, b) => b.date.localeCompare(a.date));
65
+ items.sort((a, b) => (b.date || '').localeCompare(a.date || ''));
66
66
  } else {
67
67
  items = (_kbData[_kbActiveTab] || []).map(i => ({ ...i, category: _kbActiveTab }));
68
68
  }
@@ -400,7 +400,11 @@ function _renderPlanModal(normalizedFile, raw, lastMod) {
400
400
  let text = '';
401
401
 
402
402
  if (normalizedFile.endsWith('.json')) {
403
- const plan = JSON.parse(raw);
403
+ let plan;
404
+ try { plan = JSON.parse(raw); } catch (e) {
405
+ document.getElementById('modal-body').innerHTML = '<p style="color:var(--red)">Failed to parse plan JSON: ' + escHtml(e.message) + '</p><pre style="font-size:10px;max-height:200px;overflow:auto">' + escHtml((raw || '').slice(0, 500)) + '</pre>';
406
+ return;
407
+ }
404
408
  title = plan.plan_summary || normalizedFile;
405
409
  const items = (plan.missing_features || []).map((f, i) =>
406
410
  (i + 1) + '. [' + f.id + '] ' + f.name + ' (' + (f.estimated_complexity || '?') + ', ' + (f.priority || '?') + ')' +
@@ -550,7 +550,14 @@ function showArchivedPrdDetail(idxOrFile) {
550
550
  }
551
551
 
552
552
  async function prdItemEdit(source, itemId) {
553
- const item = _prdItems.find(i => i.source === source && i.id === itemId);
553
+ let item = _prdItems.find(i => i.source === source && i.id === itemId);
554
+ // Also search archived groups if not found in active items
555
+ if (!item && window._archivedPrdGroups) {
556
+ for (const g of window._archivedPrdGroups) {
557
+ item = (g.items || []).find(i => i.source === source && i.id === itemId);
558
+ if (item) break;
559
+ }
560
+ }
554
561
  if (!item) return;
555
562
 
556
563
  // Look up work item and dispatch completion info
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.367",
3
+ "version": "0.1.369",
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"