@yemi33/minions 0.1.121 → 0.1.123

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,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.123 (2026-04-01)
4
+
5
+ ### Engine
6
+ - engine/ado-mcp-wrapper.js
7
+
8
+ ### Dashboard
9
+ - dashboard.js
10
+ - dashboard/js/detail-panel.js
11
+ - dashboard/js/modal-qa.js
12
+ - dashboard/js/render-inbox.js
13
+ - dashboard/js/render-kb.js
14
+ - dashboard/js/render-plans.js
15
+ - dashboard/js/utils.js
16
+ - dashboard/styles.css
17
+
18
+ ## 0.1.122 (2026-04-01)
19
+
20
+ ### Other
21
+ - test/unit.test.js
22
+
3
23
  ## 0.1.121 (2026-04-01)
4
24
 
5
25
  ### Dashboard
@@ -79,7 +79,16 @@ function renderDetailContent(detail, tab) {
79
79
  '</div>';
80
80
  startLiveStream(currentAgentId);
81
81
  } else if (tab === 'charter') {
82
- el.innerHTML = '<div class="section">' + renderMd(detail.charter || 'No charter found.') + '</div>';
82
+ const charterContent = detail.charter || '';
83
+ el.innerHTML =
84
+ '<div style="display:flex;gap:6px;margin-bottom:8px">' +
85
+ '<button class="pr-pager-btn" id="charter-edit-btn" style="font-size:10px;padding:2px 10px" onclick="_toggleCharterEdit()">Edit</button>' +
86
+ '<button class="pr-pager-btn" id="charter-save-btn" style="font-size:10px;padding:2px 10px;color:var(--green);border-color:var(--green);display:none" onclick="_saveCharter()">Save</button>' +
87
+ '<button class="pr-pager-btn" id="charter-cancel-btn" style="font-size:10px;padding:2px 10px;display:none" onclick="_cancelCharterEdit()">Cancel</button>' +
88
+ '</div>' +
89
+ '<div id="charter-view" class="section">' + renderMd(charterContent || 'No charter found. Click Edit to create one.') + '</div>' +
90
+ '<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;
83
92
  } else if (tab === 'history') {
84
93
  let html = '';
85
94
  // Recent dispatch results
@@ -99,11 +108,52 @@ function renderDetailContent(detail, tab) {
99
108
  html += '</tbody></table>';
100
109
  }
101
110
  // Raw history.md
102
- html += '<h4>Task History</h4><div class="section">' + escHtml(detail.history || 'No history yet.') + '</div>';
111
+ html += '<h4>Task History</h4><div class="section">' + renderMd(detail.history || 'No history yet.') + '</div>';
103
112
  el.innerHTML = html;
104
113
  } else if (tab === 'output') {
105
- el.innerHTML = '<div class="section">' + escHtml(detail.outputLog || 'No output log. The coordinator will save agent output here when tasks complete.') + '</div>';
114
+ el.innerHTML = '<div class="section">' + renderMd(detail.outputLog || 'No output log. The coordinator will save agent output here when tasks complete.') + '</div>';
106
115
  }
107
116
  }
108
117
 
118
+ function _toggleCharterEdit() {
119
+ document.getElementById('charter-view').style.display = 'none';
120
+ document.getElementById('charter-editor').style.display = '';
121
+ document.getElementById('charter-edit-btn').style.display = 'none';
122
+ document.getElementById('charter-save-btn').style.display = '';
123
+ document.getElementById('charter-cancel-btn').style.display = '';
124
+ document.getElementById('charter-editor').focus();
125
+ }
126
+
127
+ function _cancelCharterEdit() {
128
+ const el = document.getElementById('detail-content');
129
+ document.getElementById('charter-editor').value = el._charterRaw || '';
130
+ document.getElementById('charter-view').style.display = '';
131
+ document.getElementById('charter-editor').style.display = 'none';
132
+ document.getElementById('charter-edit-btn').style.display = '';
133
+ document.getElementById('charter-save-btn').style.display = 'none';
134
+ document.getElementById('charter-cancel-btn').style.display = 'none';
135
+ }
136
+
137
+ async function _saveCharter() {
138
+ const content = document.getElementById('charter-editor').value;
139
+ const btn = document.getElementById('charter-save-btn');
140
+ btn.textContent = 'Saving...'; btn.style.pointerEvents = 'none';
141
+ try {
142
+ const res = await fetch('/api/agents/charter', {
143
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
144
+ body: JSON.stringify({ agent: currentAgentId, content })
145
+ });
146
+ if (res.ok) {
147
+ document.getElementById('charter-view').innerHTML = renderMd(content);
148
+ document.getElementById('detail-content')._charterRaw = content;
149
+ _cancelCharterEdit();
150
+ showToast('cmd-toast', 'Charter saved', true);
151
+ } else {
152
+ const d = await res.json().catch(() => ({}));
153
+ alert('Save failed: ' + (d.error || 'unknown'));
154
+ }
155
+ } catch (e) { alert('Save failed: ' + e.message); }
156
+ btn.textContent = 'Save'; btn.style.pointerEvents = '';
157
+ }
158
+
109
159
  window.MinionsDetail = { closeDetail, renderDetailTabs, switchTab, renderDetailContent };
@@ -220,7 +220,7 @@ async function _processQaMessage(message, selection) {
220
220
  if (isJson) {
221
221
  body.textContent = display;
222
222
  } else {
223
- body.innerHTML = '<div style="font-size:12px;line-height:1.6">' + renderMd(display) + '</div>';
223
+ body.innerHTML = renderMd(display);
224
224
  body.style.fontFamily = "'Segoe UI', system-ui, sans-serif";
225
225
  body.style.whiteSpace = 'normal';
226
226
  }
@@ -65,7 +65,7 @@ function openNotesModal() {
65
65
  if (!preview) return;
66
66
  const content = preview._rawContent || preview.textContent;
67
67
  document.getElementById('modal-title').textContent = 'Team Notes';
68
- document.getElementById('modal-body').innerHTML = '<div style="font-size:12px;line-height:1.6">' + renderMd(content) + '</div>';
68
+ document.getElementById('modal-body').innerHTML = renderMd(content);
69
69
  document.getElementById('modal-body').style.fontFamily = "'Segoe UI', system-ui, sans-serif";
70
70
  document.getElementById('modal-body').style.whiteSpace = 'normal';
71
71
  _modalDocContext = { title: 'Team Notes', content, selection: '' };
@@ -164,9 +164,7 @@ async function kbOpenItem(category, file) {
164
164
  const display = content.replace(/^---[\s\S]*?---\n*/m, '');
165
165
  document.getElementById('modal-title').textContent = file;
166
166
  const modalBody = document.getElementById('modal-body');
167
- modalBody.innerHTML = '<div style="font-size:12px;line-height:1.6">' + renderMd(display) + '</div>';
168
- modalBody.style.fontFamily = "'Segoe UI', system-ui, sans-serif";
169
- modalBody.style.whiteSpace = 'normal';
167
+ modalBody.innerHTML = renderMd(display);
170
168
  _modalDocContext = { title: file, content: display, selection: '' };
171
169
  _modalFilePath = 'knowledge/' + category + '/' + file; showModalQa();
172
170
  // Clear notification badge when opening this document
@@ -488,9 +488,7 @@ async function planView(file) {
488
488
  modalBody.style.fontFamily = 'Consolas, monospace';
489
489
  modalBody.style.whiteSpace = 'pre-wrap';
490
490
  } else {
491
- modalBody.innerHTML = '<div style="font-size:12px;line-height:1.6">' + renderMd(text) + '</div>';
492
- modalBody.style.fontFamily = "'Segoe UI', system-ui, sans-serif";
493
- modalBody.style.whiteSpace = 'normal';
491
+ modalBody.innerHTML = renderMd(text);
494
492
  }
495
493
  _modalDocContext = { title, content: text, selection: '' };
496
494
  _modalFilePath = resolvedPath || ((normalizedFile.endsWith('.json') ? 'prd/' : 'plans/') + normalizedFile); showModalQa();
@@ -634,9 +632,7 @@ async function planOpenInDocChat(file) {
634
632
  document.getElementById('modal-body').style.fontFamily = 'Consolas, monospace';
635
633
  document.getElementById('modal-body').style.whiteSpace = 'pre-wrap';
636
634
  } else {
637
- document.getElementById('modal-body').innerHTML = '<div style="font-size:12px;line-height:1.6">' + renderMd(text) + '</div>';
638
- document.getElementById('modal-body').style.fontFamily = "'Segoe UI', system-ui, sans-serif";
639
- document.getElementById('modal-body').style.whiteSpace = 'normal';
635
+ document.getElementById('modal-body').innerHTML = renderMd(text);
640
636
  }
641
637
  _modalDocContext = { title: title, content: text, selection: '' };
642
638
  _modalFilePath = resolvedPath || ((normalizedFile.endsWith('.json') ? 'prd/' : 'plans/') + normalizedFile); showModalQa();
@@ -669,9 +665,7 @@ async function openVerifyGuide(file) {
669
665
  const content = await fetch('/api/plans/' + encodeURIComponent(normalizedFile)).then(r => r.text());
670
666
  document.getElementById('modal-title').innerHTML = 'Manual Testing Guide' +
671
667
  ' <button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;margin-left:8px;vertical-align:middle" onclick="openArchivedPrdModal()">Back</button>';
672
- document.getElementById('modal-body').innerHTML = '<div style="font-size:12px;line-height:1.6">' + renderMd(content) + '</div>';
673
- document.getElementById('modal-body').style.fontFamily = "'Segoe UI', system-ui, sans-serif";
674
- document.getElementById('modal-body').style.whiteSpace = 'normal';
668
+ document.getElementById('modal-body').innerHTML = renderMd(content);
675
669
  _modalDocContext = { title: 'Manual Testing Guide', content, selection: '' };
676
670
  _modalFilePath = 'prd/' + normalizedFile; showModalQa();
677
671
  const card = findCardForFile(_modalFilePath);
@@ -187,7 +187,7 @@ function renderMd(s) {
187
187
  // 4. Restore code placeholders
188
188
  html = html.replace(/\x00CB(\d+)\x00/g, function(_, idx) { return codeSlots[idx]; });
189
189
 
190
- return html;
190
+ return '<div class="md-content">' + html + '</div>';
191
191
  }
192
192
 
193
193
  window.MinionsUtils = { wakeEngine, escHtml, renderMd, normalizePlanFile, timeAgo, statusColor, llmCopyBtn, copyLlmText };
@@ -541,6 +541,14 @@
541
541
  }
542
542
  .detail-content h4 { color: var(--text); font-size: var(--text-lg); margin: var(--space-6) 0 var(--space-3) 0; font-family: 'Segoe UI', sans-serif; }
543
543
  .detail-content .section { margin-bottom: var(--space-7); padding: var(--space-6); background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius-md); }
544
+
545
+ /* Markdown content — consistent styling everywhere renderMd() is used */
546
+ .md-content { font-family: 'Segoe UI', system-ui, sans-serif; font-size: 12px; line-height: 1.6; white-space: normal; word-break: break-word; }
547
+ .md-content pre { font-family: Consolas, 'Courier New', monospace; white-space: pre-wrap; }
548
+ .md-content code { font-family: Consolas, 'Courier New', monospace; }
549
+ .md-content table { border-collapse: collapse; width: 100%; margin: 6px 0; }
550
+ .md-content th, .md-content td { padding: 4px 8px; border: 1px solid var(--border); text-align: left; font-size: 11px; }
551
+ .md-content th { background: var(--surface); font-weight: 600; }
544
552
  .status-line { display: flex; align-items: center; gap: var(--space-5); padding: var(--space-5) var(--space-7); background: var(--bg); border-bottom: 1px solid var(--border); font-size: var(--text-md); }
545
553
 
546
554
  /* Modal for inbox detail */
package/dashboard.js CHANGED
@@ -747,7 +747,7 @@ function spawnEngine() {
747
747
  if (key === 'CLAUDECODE' || key.startsWith('CLAUDE_CODE') || key.startsWith('CLAUDECODE_')) delete childEnv[key];
748
748
  }
749
749
  const engineProc = cpSpawn(process.execPath, [path.join(MINIONS_DIR, 'engine.js'), 'start'], {
750
- cwd: MINIONS_DIR, stdio: 'ignore', detached: true, env: childEnv,
750
+ cwd: MINIONS_DIR, stdio: 'ignore', detached: true, env: childEnv, windowsHide: true,
751
751
  });
752
752
  engineProc.unref();
753
753
  return engineProc.pid;
@@ -758,7 +758,7 @@ function killEnginePid(pid) {
758
758
  try {
759
759
  const safePid = shared.validatePid(pid);
760
760
  if (process.platform === 'win32') {
761
- execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000 });
761
+ execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000, windowsHide: true });
762
762
  } else {
763
763
  process.kill(safePid, 'SIGKILL');
764
764
  }
@@ -1271,7 +1271,7 @@ const server = http.createServer(async (req, res) => {
1271
1271
  try {
1272
1272
  const safePid = shared.validatePid(status.pid);
1273
1273
  if (process.platform === 'win32') {
1274
- require('child_process').execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000 });
1274
+ require('child_process').execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000, windowsHide: true });
1275
1275
  } else {
1276
1276
  process.kill(safePid, 'SIGTERM');
1277
1277
  }
@@ -1866,7 +1866,7 @@ If nothing to do: { "duplicates": [], "reclassify": [], "remove": [] }`;
1866
1866
  try {
1867
1867
  const safePid = shared.validatePid(agentStatus.pid);
1868
1868
  if (process.platform === 'win32') {
1869
- require('child_process').execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000 });
1869
+ require('child_process').execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000, windowsHide: true });
1870
1870
  } else {
1871
1871
  process.kill(safePid, 'SIGTERM');
1872
1872
  }
@@ -2567,7 +2567,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
2567
2567
  try {
2568
2568
  const safePid = shared.validatePid(agentStatus.pid);
2569
2569
  if (process.platform === 'win32') {
2570
- require('child_process').execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000 });
2570
+ require('child_process').execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000, windowsHide: true });
2571
2571
  } else {
2572
2572
  process.kill(safePid, 'SIGTERM');
2573
2573
  }
@@ -3602,7 +3602,7 @@ server.listen(PORT, '127.0.0.1', () => {
3602
3602
  let alive = false;
3603
3603
  try {
3604
3604
  if (process.platform === 'win32') {
3605
- const out = execSync(`tasklist /FI "PID eq ${control.pid}" /NH`, { encoding: 'utf8', timeout: 3000 });
3605
+ const out = execSync(`tasklist /FI "PID eq ${control.pid}" /NH`, { encoding: 'utf8', timeout: 3000, windowsHide: true });
3606
3606
  alive = out.includes(String(control.pid));
3607
3607
  } else {
3608
3608
  process.kill(control.pid, 0); // signal 0 = check existence
@@ -33,6 +33,7 @@ const child = spawn(process.platform === 'win32' ? 'npx.cmd' : 'npx', [
33
33
  stdio: 'inherit',
34
34
  env: { ...process.env, AZURE_DEVOPS_EXT_PAT: token },
35
35
  windowsHide: true,
36
+ shell: false,
36
37
  });
37
38
 
38
39
  child.on('exit', (code) => process.exit(code || 0));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.121",
3
+ "version": "0.1.123",
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"