@yemi33/minions 0.1.365 → 0.1.367

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,8 +1,10 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.365 (2026-04-06)
3
+ ## 0.1.367 (2026-04-06)
4
4
 
5
5
  ### Fixes
6
+ - low-severity dashboard polish — null guards, event params, UI gaps (#213)
7
+ - medium dashboard bugs — settings reset, work items, schedules, plans (#212)
6
8
  - XSS vulnerabilities across dashboard components (#211)
7
9
 
8
10
  ## 0.1.364 (2026-04-06)
@@ -6,9 +6,9 @@ let _completedPage = 0;
6
6
  let _logPage = 0;
7
7
 
8
8
  function _completedPrev() { if (_completedPage > 0) { _completedPage--; refresh(); } }
9
- function _completedNext() { _completedPage++; refresh(); }
9
+ function _completedNext() { _completedPage++; refresh(); } // clamped in renderDispatch
10
10
  function _logPrev() { if (_logPage > 0) { _logPage--; refresh(); } }
11
- function _logNext() { _logPage++; refresh(); }
11
+ function _logNext() { _logPage++; refresh(); } // clamped in renderEngineLog
12
12
 
13
13
  function renderEngineStatus(engine) {
14
14
  const badge = document.getElementById('engine-badge');
@@ -153,6 +153,7 @@ function renderDispatch(dispatch) {
153
153
 
154
154
  function renderEngineLog(log) {
155
155
  const el = document.getElementById('engine-log');
156
+ if (!el) return;
156
157
  if (!log || log.length === 0) {
157
158
  el.innerHTML = '<div class="empty">No log entries yet.</div>';
158
159
  return;
@@ -140,7 +140,7 @@ async function modalSaveEdit() {
140
140
  function modalCancelEdit() {
141
141
  const body = document.getElementById('modal-body');
142
142
  body.contentEditable = 'false';
143
- body.textContent = _modalDocContext.content; // revert
143
+ body.innerHTML = renderMd(_modalDocContext.content); // revert (render Markdown, not raw text)
144
144
  body.style.border = '';
145
145
  body.style.padding = '';
146
146
  document.getElementById('modal-edit-btn').style.display = '';
@@ -255,7 +255,7 @@ function renderPlans(plans) {
255
255
  '<div><div class="plan-card-title">' + escHtml(p.summary || p.file) + versionBadge + '</div>' +
256
256
  '<div class="plan-card-meta">' +
257
257
  '<span style="font-weight:600;color:' + (statusColors[effectiveStatus] || 'var(--muted)') + '">' + label + '</span>' +
258
- '<span>' + escHtml(p.project) + '</span>' +
258
+ (p.project ? '<span>' + escHtml(p.project) + '</span>' : '') +
259
259
  '<span>' + p.itemCount + ' items</span>' +
260
260
  (p.updatedAt ? '<span title="Last updated: ' + p.updatedAt + '">Updated ' + timeAgo(p.updatedAt) + '</span>' : '') +
261
261
  (p.completedAt ? '<span>' + p.completedAt.slice(0, 10) + '</span>' : '') +
@@ -370,6 +370,7 @@ async function planSubmitRevise(file) {
370
370
  try {
371
371
  const res = await fetch('/api/plans/revise', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ file, feedback }) });
372
372
  const data = await res.json();
373
+ if (!res.ok) { showToast('cmd-toast', 'Revision failed: ' + (data.error || 'unknown'), false); return; }
373
374
  showToast('cmd-toast', 'Revision requested — agent will update the plan (' + data.workItemId + ')', true);
374
375
  planHideRevise(file);
375
376
  refreshPlans();
@@ -96,9 +96,10 @@ function openModal(i) {
96
96
  }
97
97
 
98
98
  function openAddPrModal() {
99
- const projOpts = (typeof cmdProjects !== 'undefined' ? cmdProjects : []).map(p =>
100
- '<option value="' + escHtml(p) + '">' + escHtml(p) + '</option>'
101
- ).join('');
99
+ const projOpts = (typeof cmdProjects !== 'undefined' ? cmdProjects : []).map(p => {
100
+ const name = typeof p === 'object' ? p.name : p;
101
+ return '<option value="' + escHtml(name) + '">' + escHtml(name) + '</option>';
102
+ }).join('');
102
103
  const inputStyle = 'display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:var(--text-md);font-family:inherit';
103
104
 
104
105
  document.getElementById('modal-title').textContent = 'Link Pull Request';
@@ -484,6 +484,7 @@ function openEditScheduleModal(id) {
484
484
  document.getElementById('modal-body').innerHTML = _scheduleFormHtml(sched, true);
485
485
  window._editScheduleId = id;
486
486
  document.getElementById('modal').classList.add('open');
487
+ _updateCronPreview();
487
488
  }
488
489
 
489
490
  async function submitSchedule(isEdit) {
@@ -84,7 +84,7 @@ function renderWorkItems(items) {
84
84
  allWorkItems = items;
85
85
  const el = document.getElementById('work-items-content');
86
86
  const countEl = document.getElementById('wi-count');
87
- countEl.textContent = items.length;
87
+ if (countEl) countEl.textContent = items.length;
88
88
  if (!items.length) {
89
89
  el.innerHTML = '<p class="empty">No work items. Add tasks via Command Center above.</p>';
90
90
  return;
@@ -151,14 +151,14 @@ function editWorkItem(id, source) {
151
151
  '</label>' +
152
152
  '<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:8px">' +
153
153
  '<button onclick="closeModal()" class="pr-pager-btn" style="padding:6px 16px;font-size:var(--text-md)">Cancel</button>' +
154
- '<button onclick="submitWorkItemEdit(\'' + escHtml(id) + '\',\'' + escHtml(source || '') + '\')" style="padding:6px 16px;font-size:var(--text-md);background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Save</button>' +
154
+ '<button onclick="submitWorkItemEdit(\'' + escHtml(id) + '\',\'' + escHtml(source || '') + '\',event)" style="padding:6px 16px;font-size:var(--text-md);background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Save</button>' +
155
155
  '</div>' +
156
156
  '</div>';
157
157
  document.getElementById('modal').classList.add('open');
158
158
  }
159
159
 
160
- async function submitWorkItemEdit(id, source) {
161
- var btn = event?.target; if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; }
160
+ async function submitWorkItemEdit(id, source, e) {
161
+ var btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; }
162
162
  const title = document.getElementById('wi-edit-title').value.trim();
163
163
  const description = document.getElementById('wi-edit-desc').value;
164
164
  const type = document.getElementById('wi-edit-type').value;
@@ -320,7 +320,7 @@ function openCreateWorkItemModal() {
320
320
  const typeOpts = ['implement', 'fix', 'explore', 'test', 'review', 'ask', 'plan', 'verify', 'decompose', 'meeting'].map(t =>
321
321
  '<option value="' + t + '"' + (t === 'implement' ? ' selected' : '') + '>' + t + '</option>'
322
322
  ).join('');
323
- const priOpts = ['high', 'medium', 'low'].map(p =>
323
+ const priOpts = ['critical', 'high', 'medium', 'low'].map(p =>
324
324
  '<option value="' + p + '"' + (p === 'medium' ? ' selected' : '') + '>' + p + '</option>'
325
325
  ).join('');
326
326
  const agentOpts = (typeof cmdAgents !== 'undefined' ? cmdAgents : []).map(a =>
@@ -349,7 +349,7 @@ function openCreateWorkItemModal() {
349
349
  '<label id="wi-new-skippr-row" style="color:var(--text);font-size:var(--text-md);display:flex;gap:8px;align-items:center;cursor:pointer"><input type="checkbox" id="wi-new-skippr"> Skip PR creation (push branch only)</label>' +
350
350
  '<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:4px">' +
351
351
  '<button onclick="closeModal()" class="pr-pager-btn">Cancel</button>' +
352
- '<button onclick="_submitCreateWorkItem()" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Create</button>' +
352
+ '<button onclick="_submitCreateWorkItem(event)" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Create</button>' +
353
353
  '</div>' +
354
354
  '</div>';
355
355
  document.getElementById('modal').classList.add('open');
@@ -365,8 +365,8 @@ function openCreateWorkItemModal() {
365
365
  setTimeout(() => document.getElementById('wi-new-title')?.focus(), 100);
366
366
  }
367
367
 
368
- async function _submitCreateWorkItem() {
369
- var btn = event?.target; if (btn) { btn.disabled = true; btn.textContent = 'Creating...'; }
368
+ async function _submitCreateWorkItem(e) {
369
+ var btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = 'Creating...'; }
370
370
  const title = document.getElementById('wi-new-title')?.value?.trim();
371
371
  if (!title) { if (btn) { btn.disabled = false; btn.textContent = 'Create'; } alert('Title is required'); return; }
372
372
  const desc = document.getElementById('wi-new-desc')?.value || '';
@@ -38,7 +38,7 @@ function timeAgo(isoStr) {
38
38
  }
39
39
 
40
40
  function statusColor(s) {
41
- return s === 'working' ? 'working' : s === 'done' ? 'done' : '';
41
+ return s === 'working' ? 'working' : s === 'done' ? 'done' : s === 'error' ? 'error' : '';
42
42
  }
43
43
 
44
44
  function llmCopyBtn() {
package/dashboard.js CHANGED
@@ -959,15 +959,18 @@ const server = http.createServer(async (req, res) => {
959
959
  }
960
960
  if (!wiPath) return jsonReply(res, 404, { error: 'source not found' });
961
961
 
962
- const items = JSON.parse(safeRead(wiPath) || '[]');
963
- const idx = items.findIndex(i => i.id === id);
964
- if (idx === -1) return jsonReply(res, 404, { error: 'item not found' });
965
-
966
- const item = items[idx];
967
-
968
- // Remove item from work-items file
969
- items.splice(idx, 1);
970
- safeWrite(wiPath, items);
962
+ let item = null;
963
+ let found = false;
964
+ mutateJsonFileLocked(wiPath, (items) => {
965
+ if (!Array.isArray(items)) return items;
966
+ const idx = items.findIndex(i => i.id === id);
967
+ if (idx === -1) return items;
968
+ item = items[idx];
969
+ items.splice(idx, 1);
970
+ found = true;
971
+ return items;
972
+ }, { defaultValue: [] });
973
+ if (!found) return jsonReply(res, 404, { error: 'item not found' });
971
974
 
972
975
  // Clean dispatch entries + kill running agent
973
976
  const dispatchRemoved = cleanDispatchEntries(d =>
@@ -1080,6 +1083,7 @@ const server = http.createServer(async (req, res) => {
1080
1083
  if (body.agents) item.agents = body.agents;
1081
1084
  if (body.references) item.references = body.references;
1082
1085
  if (body.acceptanceCriteria) item.acceptanceCriteria = body.acceptanceCriteria;
1086
+ if (body.skipPr) item.skipPr = true;
1083
1087
  items.push(item);
1084
1088
  safeWrite(wiPath, items);
1085
1089
  return jsonReply(res, 200, { ok: true, id });
@@ -3297,6 +3301,8 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3297
3301
  config.claude = { ...shared.DEFAULT_CLAUDE };
3298
3302
  config.agents = { ...shared.DEFAULT_AGENTS };
3299
3303
  safeWrite(path.join(MINIONS_DIR, 'config.json'), config);
3304
+ reloadConfig();
3305
+ invalidateStatusCache();
3300
3306
  return jsonReply(res, 200, { ok: true });
3301
3307
  } catch (e) { return jsonReply(res, 500, { error: e.message }); }
3302
3308
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.365",
3
+ "version": "0.1.367",
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"