@yemi33/minions 0.1.138 → 0.1.140

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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.140 (2026-04-01)
4
+
5
+ ### Dashboard
6
+ - dashboard.js
7
+ - dashboard/js/command-center.js
8
+ - dashboard/js/render-pipelines.js
9
+
10
+ ## 0.1.139 (2026-04-01)
11
+
12
+ ### Dashboard
13
+ - dashboard/js/render-pipelines.js
14
+
3
15
  ## 0.1.138 (2026-04-01)
4
16
 
5
17
  ### Dashboard
@@ -434,6 +434,20 @@ async function ccExecuteAction(action) {
434
434
  status.style.color = 'var(--green)';
435
435
  break;
436
436
  }
437
+ case 'edit-pipeline': {
438
+ const body = { id: action.id };
439
+ if (action.title) body.title = action.title;
440
+ if (action.stages) body.stages = action.stages;
441
+ if (action.trigger !== undefined) body.trigger = action.trigger;
442
+ const res = await fetch('/api/pipelines/update', {
443
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
444
+ body: JSON.stringify(body)
445
+ });
446
+ if (!res.ok) { const d = await res.json().catch(() => ({})); throw new Error(d.error || 'Pipeline update failed'); }
447
+ status.innerHTML = '&#10003; Updated pipeline: <strong>' + escHtml(action.id) + '</strong>';
448
+ status.style.color = 'var(--green)';
449
+ break;
450
+ }
437
451
  default:
438
452
  status.innerHTML = '? Unknown action: ' + escHtml(action.type);
439
453
  status.style.color = 'var(--muted)';
@@ -19,7 +19,7 @@ function renderPipelines(pipelines) {
19
19
  const lastRun = (p.runs || []).slice(-1)[0];
20
20
  const statusColor = activeRun ? 'var(--blue)' : lastRun?.status === 'completed' ? 'var(--green)' : lastRun?.status === 'failed' ? 'var(--red)' : 'var(--muted)';
21
21
  const statusLabel = activeRun ? 'Running' : lastRun ? (lastRun.status === 'completed' ? 'Completed' : lastRun.status === 'failed' ? 'Failed' : lastRun.status) : 'Never run';
22
- const trigger = p.trigger?.cron ? 'Cron: ' + p.trigger.cron : 'Manual';
22
+ const trigger = p.trigger?.cron ? _cronToHuman(p.trigger.cron) : 'Manual';
23
23
 
24
24
  // Stage flow visualization
25
25
  var stageFlow = (p.stages || []).map(function(s) {
@@ -52,9 +52,10 @@ function openPipelineDetail(id) {
52
52
  // Status + actions
53
53
  var activeRun = (p.runs || []).find(function(r) { return r.status === 'running'; });
54
54
  html += '<div style="display:flex;justify-content:space-between;align-items:center">' +
55
- '<span style="font-size:10px;color:var(--muted)">' + (p.trigger?.cron ? 'Cron: ' + p.trigger.cron : 'Manual trigger') + '</span>' +
55
+ '<span style="font-size:10px;color:var(--muted)">' + (p.trigger?.cron ? escHtml(_cronToHuman(p.trigger.cron)) + ' <span style="opacity:0.6">(' + escHtml(p.trigger.cron) + ', ' + escHtml(Intl.DateTimeFormat().resolvedOptions().timeZone) + ')</span>' : 'Manual trigger') + '</span>' +
56
56
  '<div style="display:flex;gap:6px">' +
57
57
  (activeRun ? '' : '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;color:var(--green);border-color:var(--green)" onclick="_triggerPipeline(\'' + escHtml(id) + '\',this)">Run Now</button>') +
58
+ '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;color:var(--blue);border-color:var(--blue)" onclick="openEditPipelineModal(\'' + escHtml(id) + '\')">Edit</button>' +
58
59
  '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px" onclick="_togglePipelineEnabled(\'' + escHtml(id) + '\',' + !p.enabled + ',this)">' + (p.enabled !== false ? 'Disable' : 'Enable') + '</button>' +
59
60
  '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;color:var(--red);border-color:var(--red)" onclick="_deletePipelineConfirm(\'' + escHtml(id) + '\')">Delete</button>' +
60
61
  '</div>' +
@@ -142,13 +143,52 @@ async function _deletePipelineConfirm(id) {
142
143
 
143
144
  function openCreatePipelineModal() {
144
145
  var 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';
146
+ var pillStyle = 'display:inline-block;padding:4px 10px;margin:2px;border:1px solid var(--border);border-radius:12px;cursor:pointer;font-size:11px;color:var(--text);background:var(--bg);user-select:none;transition:all 0.15s';
147
+ var linkStyle = 'font-size:10px;color:var(--blue);cursor:pointer;text-decoration:underline;margin-right:8px';
148
+ var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
149
+
150
+ // Hour options (0-23)
151
+ var hourOpts = '';
152
+ for (var h = 0; h < 24; h++) hourOpts += '<option value="' + h + '"' + (h === 9 ? ' selected' : '') + '>' + String(h).padStart(2, '0') + '</option>';
153
+ // Minute options (0, 5, 10, ..., 55)
154
+ var minOpts = '';
155
+ for (var m = 0; m <= 55; m += 5) minOpts += '<option value="' + m + '"' + (m === 0 ? ' selected' : '') + '>' + String(m).padStart(2, '0') + '</option>';
156
+
157
+ var dayOrder = [0, 1, 2, 3, 4, 5, 6];
158
+ var dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
159
+ var dayPills = dayOrder.map(function(d) {
160
+ var active = d >= 1 && d <= 5; // default weekdays
161
+ return '<span class="sched-day-pill' + (active ? ' active' : '') + '" data-day="' + d + '" ' +
162
+ 'style="' + pillStyle + (active ? ';background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" ' +
163
+ 'onclick="_toggleDayPill(this);_updatePlCronPreview()">' + dayLabels[d] + '</span>';
164
+ }).join('');
145
165
 
146
166
  document.getElementById('modal-title').textContent = 'New Pipeline';
147
167
  document.getElementById('modal-body').innerHTML =
148
168
  '<div style="display:flex;flex-direction:column;gap:10px">' +
149
169
  '<label style="color:var(--text);font-size:var(--text-md)">ID<input id="pl-id" style="' + inputStyle + '" placeholder="e.g. daily-audit-cycle"></label>' +
150
170
  '<label style="color:var(--text);font-size:var(--text-md)">Title<input id="pl-title" style="' + inputStyle + '" placeholder="e.g. Daily audit and improvement cycle"></label>' +
151
- '<label style="color:var(--text);font-size:var(--text-md)">Trigger (cron, optional)<input id="pl-cron" style="' + inputStyle + '" placeholder="e.g. 0 9 * (9am daily) — leave empty for manual"></label>' +
171
+ '<div>' +
172
+ '<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">' +
173
+ '<label style="color:var(--text);font-size:var(--text-md);margin:0">Schedule</label>' +
174
+ '<label style="font-size:11px;color:var(--muted);cursor:pointer"><input type="checkbox" id="pl-use-cron" checked onchange="_updatePlCronPreview()" style="accent-color:var(--blue)"> Enable automatic trigger</label>' +
175
+ '</div>' +
176
+ '<div id="pl-cron-picker">' +
177
+ '<div style="display:flex;gap:8px;align-items:center">' +
178
+ '<select id="pl-pick-hour" style="' + inputStyle + ';width:auto;display:inline-block" onchange="_updatePlCronPreview()">' + hourOpts + '</select>' +
179
+ '<span style="color:var(--muted)">:</span>' +
180
+ '<select id="pl-pick-minute" style="' + inputStyle + ';width:auto;display:inline-block" onchange="_updatePlCronPreview()">' + minOpts + '</select>' +
181
+ '<span style="font-size:10px;color:var(--muted)">' + escHtml(tz) + '</span>' +
182
+ '</div>' +
183
+ '<div style="margin-top:8px">' + dayPills + '</div>' +
184
+ '<div style="margin-top:6px">' +
185
+ '<span style="' + linkStyle + '" onclick="_quickSelectDays(\'all\');_updatePlCronPreview()">Every day</span>' +
186
+ '<span style="' + linkStyle + '" onclick="_quickSelectDays(\'weekdays\');_updatePlCronPreview()">Weekdays</span>' +
187
+ '<span style="' + linkStyle + '" onclick="_quickSelectDays(\'weekends\');_updatePlCronPreview()">Weekends</span>' +
188
+ '</div>' +
189
+ '<div id="pl-cron-preview" style="margin-top:4px;font-size:11px;color:var(--blue)"></div>' +
190
+ '</div>' +
191
+ '</div>' +
152
192
  '<label style="color:var(--text);font-size:var(--text-md)">Stages (JSON array)<textarea id="pl-stages" rows="10" style="' + inputStyle + ';resize:vertical;font-family:Consolas,monospace" placeholder=\'[{"id":"audit","type":"task","title":"Audit codebase","taskType":"explore"},{"id":"discuss","type":"meeting","title":"Discuss findings","dependsOn":["audit"],"participants":["all"]}]\'></textarea></label>' +
153
193
  '<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:4px">' +
154
194
  '<button onclick="closeModal()" class="pr-pager-btn">Cancel</button>' +
@@ -156,12 +196,30 @@ function openCreatePipelineModal() {
156
196
  '</div>' +
157
197
  '</div>';
158
198
  document.getElementById('modal').classList.add('open');
199
+ _updatePlCronPreview();
200
+ }
201
+
202
+ function _updatePlCronPreview() {
203
+ var useCron = document.getElementById('pl-use-cron')?.checked;
204
+ var pickerEl = document.getElementById('pl-cron-picker');
205
+ if (pickerEl) pickerEl.style.opacity = useCron ? '1' : '0.4';
206
+ if (pickerEl) pickerEl.style.pointerEvents = useCron ? '' : 'none';
207
+ if (!useCron) { window._plComputedCron = ''; var prev = document.getElementById('pl-cron-preview'); if (prev) prev.textContent = 'Manual trigger only'; return; }
208
+ var hour = parseInt(document.getElementById('pl-pick-hour')?.value || '9', 10);
209
+ var minute = parseInt(document.getElementById('pl-pick-minute')?.value || '0', 10);
210
+ var days = [];
211
+ document.querySelectorAll('.sched-day-pill').forEach(function(btn) { if (btn.classList.contains('active')) days.push(parseInt(btn.dataset.day, 10)); });
212
+ var cron = _pickerToCron(hour, minute, days);
213
+ window._plComputedCron = cron;
214
+ var prev = document.getElementById('pl-cron-preview');
215
+ if (prev) prev.textContent = cron ? '\u2192 ' + _cronToHuman(cron) : '(select at least one day)';
159
216
  }
160
217
 
161
218
  async function _submitCreatePipeline() {
162
219
  var id = document.getElementById('pl-id')?.value?.trim();
163
220
  var title = document.getElementById('pl-title')?.value?.trim();
164
- var cron = document.getElementById('pl-cron')?.value?.trim();
221
+ var useCron = document.getElementById('pl-use-cron')?.checked;
222
+ var cron = useCron ? (window._plComputedCron || '') : '';
165
223
  var stagesRaw = document.getElementById('pl-stages')?.value?.trim();
166
224
  if (!id || !title) { alert('ID and title required'); return; }
167
225
  var stages;
@@ -179,4 +237,88 @@ async function _submitCreatePipeline() {
179
237
  } catch (e) { alert('Error: ' + e.message); openCreatePipelineModal(); }
180
238
  }
181
239
 
182
- window.MinionsPipelines = { renderPipelines, openPipelineDetail, openCreatePipelineModal };
240
+ function openEditPipelineModal(id) {
241
+ var p = _pipelinesData.find(function(x) { return x.id === id; });
242
+ if (!p) return;
243
+ var 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';
244
+ var pillStyle = 'display:inline-block;padding:4px 10px;margin:2px;border:1px solid var(--border);border-radius:12px;cursor:pointer;font-size:11px;color:var(--text);background:var(--bg);user-select:none;transition:all 0.15s';
245
+ var linkStyle = 'font-size:10px;color:var(--blue);cursor:pointer;text-decoration:underline;margin-right:8px';
246
+ var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
247
+ var hasCron = !!p.trigger?.cron;
248
+ var picker = hasCron ? _parseCronToPicker(p.trigger.cron) : { hour: 9, minute: 0, days: [1,2,3,4,5] };
249
+
250
+ var hourOpts = '';
251
+ for (var h = 0; h < 24; h++) hourOpts += '<option value="' + h + '"' + (h === picker.hour ? ' selected' : '') + '>' + String(h).padStart(2, '0') + '</option>';
252
+ var minOpts = '';
253
+ for (var m = 0; m <= 55; m += 5) minOpts += '<option value="' + m + '"' + (m === picker.minute ? ' selected' : '') + '>' + String(m).padStart(2, '0') + '</option>';
254
+
255
+ var dayOrder = [0, 1, 2, 3, 4, 5, 6];
256
+ var dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
257
+ var dayPills = dayOrder.map(function(d) {
258
+ var active = picker.days.includes(d);
259
+ return '<span class="sched-day-pill' + (active ? ' active' : '') + '" data-day="' + d + '" ' +
260
+ 'style="' + pillStyle + (active ? ';background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" ' +
261
+ 'onclick="_toggleDayPill(this);_updatePlCronPreview()">' + dayLabels[d] + '</span>';
262
+ }).join('');
263
+
264
+ window._editPipelineId = id;
265
+
266
+ document.getElementById('modal-title').textContent = 'Edit Pipeline: ' + p.title;
267
+ document.getElementById('modal-body').innerHTML =
268
+ '<div style="display:flex;flex-direction:column;gap:10px">' +
269
+ '<div style="color:var(--muted);font-size:11px">ID: <strong style="color:var(--text)">' + escHtml(id) + '</strong></div>' +
270
+ '<label style="color:var(--text);font-size:var(--text-md)">Title<input id="pl-title" value="' + escHtml(p.title || '') + '" style="' + inputStyle + '"></label>' +
271
+ '<div>' +
272
+ '<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">' +
273
+ '<label style="color:var(--text);font-size:var(--text-md);margin:0">Schedule</label>' +
274
+ '<label style="font-size:11px;color:var(--muted);cursor:pointer"><input type="checkbox" id="pl-use-cron"' + (hasCron ? ' checked' : '') + ' onchange="_updatePlCronPreview()" style="accent-color:var(--blue)"> Enable automatic trigger</label>' +
275
+ '</div>' +
276
+ '<div id="pl-cron-picker">' +
277
+ '<div style="display:flex;gap:8px;align-items:center">' +
278
+ '<select id="pl-pick-hour" style="' + inputStyle + ';width:auto;display:inline-block" onchange="_updatePlCronPreview()">' + hourOpts + '</select>' +
279
+ '<span style="color:var(--muted)">:</span>' +
280
+ '<select id="pl-pick-minute" style="' + inputStyle + ';width:auto;display:inline-block" onchange="_updatePlCronPreview()">' + minOpts + '</select>' +
281
+ '<span style="font-size:10px;color:var(--muted)">' + escHtml(tz) + '</span>' +
282
+ '</div>' +
283
+ '<div style="margin-top:8px">' + dayPills + '</div>' +
284
+ '<div style="margin-top:6px">' +
285
+ '<span style="' + linkStyle + '" onclick="_quickSelectDays(\'all\');_updatePlCronPreview()">Every day</span>' +
286
+ '<span style="' + linkStyle + '" onclick="_quickSelectDays(\'weekdays\');_updatePlCronPreview()">Weekdays</span>' +
287
+ '<span style="' + linkStyle + '" onclick="_quickSelectDays(\'weekends\');_updatePlCronPreview()">Weekends</span>' +
288
+ '</div>' +
289
+ '<div id="pl-cron-preview" style="margin-top:4px;font-size:11px;color:var(--blue)"></div>' +
290
+ '</div>' +
291
+ '</div>' +
292
+ '<label style="color:var(--text);font-size:var(--text-md)">Stages (JSON array)<textarea id="pl-stages" rows="10" style="' + inputStyle + ';resize:vertical;font-family:Consolas,monospace">' + escHtml(JSON.stringify(p.stages || [], null, 2)) + '</textarea></label>' +
293
+ '<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:4px">' +
294
+ '<button onclick="closeModal()" class="pr-pager-btn">Cancel</button>' +
295
+ '<button onclick="_submitEditPipeline()" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Save</button>' +
296
+ '</div>' +
297
+ '</div>';
298
+ document.getElementById('modal-body').style.whiteSpace = 'normal';
299
+ document.getElementById('modal-body').style.fontFamily = '';
300
+ document.getElementById('modal').classList.add('open');
301
+ _updatePlCronPreview();
302
+ }
303
+
304
+ async function _submitEditPipeline() {
305
+ var id = window._editPipelineId;
306
+ if (!id) return;
307
+ var title = document.getElementById('pl-title')?.value?.trim();
308
+ var useCron = document.getElementById('pl-use-cron')?.checked;
309
+ var cron = useCron ? (window._plComputedCron || '') : '';
310
+ var stagesRaw = document.getElementById('pl-stages')?.value?.trim();
311
+ if (!title) { alert('Title required'); return; }
312
+ var stages;
313
+ try { stages = JSON.parse(stagesRaw); } catch (e) { alert('Invalid JSON in stages: ' + e.message); return; }
314
+ if (!Array.isArray(stages) || stages.length === 0) { alert('Stages must be a non-empty array'); return; }
315
+
316
+ var body = { id: id, title: title, stages: stages, trigger: cron ? { cron: cron } : null };
317
+ try {
318
+ var res = await fetch('/api/pipelines/update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
319
+ if (res.ok) { closeModal(); showToast('cmd-toast', 'Pipeline updated', true); refresh(); }
320
+ else { var d = await res.json().catch(function() { return {}; }); alert('Failed: ' + (d.error || 'unknown')); }
321
+ } catch (e) { alert('Error: ' + e.message); }
322
+ }
323
+
324
+ window.MinionsPipelines = { renderPipelines, openPipelineDetail, openCreatePipelineModal, openEditPipelineModal };
package/dashboard.js CHANGED
@@ -387,6 +387,7 @@ Available action types:
387
387
  - **delete-schedule**: Delete a scheduled task. Fields: id.
388
388
  - **create-meeting**: Start a team meeting. Fields: title (short meeting name), agenda (detailed text — what agents should investigate/debate, numbered items work best), agents (array of agent IDs), rounds (optional, default 3), project (optional).
389
389
  - **set-config**: Update engine settings. Fields: setting (setting name), value (new value). Valid settings: autoApprovePlans (bool), autoDecompose (bool), allowTempAgents (bool), maxConcurrent (number), maxTurns (number). Example: { "type": "set-config", "setting": "autoApprovePlans", "value": true }
390
+ - **edit-pipeline**: Update an existing pipeline. Fields: id (pipeline ID), title (optional), stages (optional, JSON array), trigger (optional, { cron: "minute hour dow" } or null for manual).
390
391
 
391
392
  ## Rules
392
393
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.138",
3
+ "version": "0.1.140",
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"