@yemi33/minions 0.1.686 → 0.1.688

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,14 +1,19 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.686 (2026-04-09)
3
+ ## 0.1.688 (2026-04-09)
4
4
 
5
5
  ### Features
6
+ - PR review/fix cycle pipeline, fix node rendering for never-run pipelines
7
+ - modal back button for artifact navigation
6
8
  - collapsible doc-chat thread in modal
7
9
 
8
10
  ### Fixes
9
11
  - add archive fallback for artifact notes when KB entry was swept
10
12
  - link work item artifacts to KB entries, not raw archive copies
11
13
 
14
+ ### Other
15
+ - refactor: remove dead stageFlow code from pipeline card rendering
16
+
12
17
  ## 0.1.683 (2026-04-09)
13
18
 
14
19
  ### Features
@@ -4,6 +4,7 @@ function closeModal() {
4
4
  const modalEl = document.querySelector('#modal .modal');
5
5
  if (modalEl) modalEl.classList.remove('modal-wide');
6
6
  document.getElementById('modal').classList.remove('open');
7
+ clearModalBackStack();
7
8
  // Hide Q&A section (only shown for document modals)
8
9
  document.getElementById('modal-qa').style.display = 'none';
9
10
  // Remove settings buttons if present
@@ -241,10 +241,13 @@ function _buildNodeChain(stages, run, options) {
241
241
 
242
242
  html += '</div>';
243
243
 
244
- // Loop indicator
245
- if (pipeline?.trigger?.cron) {
244
+ // Loop indicator — only for pipelines with stopWhen or condition stages (repeat-until pattern)
245
+ var hasStopWhen = !!pipeline?.stopWhen;
246
+ var hasConditionStage = (pipeline?.stages || []).some(function(s) { return s.type === 'condition'; });
247
+ if (hasStopWhen || hasConditionStage) {
246
248
  var runCount = (pipeline.runs || []).length;
247
- html += '<div class="pl-node-loop">\u21BA Loop (' + escHtml(_cronToHuman(pipeline.trigger.cron)) + ')';
249
+ var cronLabel = pipeline?.trigger?.cron ? _cronToHuman(pipeline.trigger.cron) : 'until condition met';
250
+ html += '<div class="pl-node-loop">\u21BA Loop (' + escHtml(cronLabel) + ')';
248
251
  if (runCount > 0) html += ' \u00b7 Run ' + runCount;
249
252
  html += '</div>';
250
253
  }
@@ -271,18 +274,10 @@ function renderPipelines(pipelines) {
271
274
  const statusLabel = activeRun ? 'Running' : lastRun ? (lastRun.status === 'completed' ? 'Completed' : lastRun.status === 'failed' ? 'Failed' : lastRun.status === 'stopped' ? 'Stopped' : lastRun.status) : 'Never run';
272
275
  const trigger = p.trigger?.cron ? _cronToHuman(p.trigger.cron) : 'Manual';
273
276
 
274
- // Stage flow visualization
275
- var stageFlow = (p.stages || []).map(function(s) {
276
- var icon = { task: '\u2699', meeting: '\uD83D\uDCAC', plan: '\uD83D\uDCCB', 'merge-prs': '\uD83D\uDD00', api: '\uD83C\uDF10', wait: '\u23F8', parallel: '\u2693', schedule: '\u23F0', condition: '\u2753' }[s.type] || '\u2022';
277
- var stageStatus = activeRun?.stages?.[s.id]?.status || 'pending';
278
- var color = stageStatus === 'completed' ? 'var(--green)' : stageStatus === 'running' ? 'var(--blue)' : stageStatus === 'failed' ? 'var(--red)' : stageStatus === 'waiting-human' ? 'var(--yellow)' : 'var(--muted)';
279
- return '<span style="color:' + color + ';font-size:11px" title="' + escHtml(s.id) + ': ' + escHtml(s.title || s.type) + ' (' + stageStatus + ')">' + icon + ' ' + escHtml(s.id) + '</span>';
280
- }).join(' <span style="color:var(--border)">\u2192</span> ');
281
-
282
- // Build step-progress indicator for pipelines with a run
277
+ // Build node chain (renders for all pipelines, even never-run)
283
278
  var progressHtml = '';
284
279
  var displayRun = activeRun || lastRun;
285
- if (displayRun && (p.stages || []).length > 0) {
280
+ if ((p.stages || []).length > 0) {
286
281
  progressHtml = _buildNodeChain(p.stages || [], displayRun, { compact: true, pipeline: p });
287
282
  }
288
283
 
@@ -330,7 +325,7 @@ function openPipelineDetail(id) {
330
325
 
331
326
  // Stage detail with progress bar
332
327
  var detailRun = activeRun || (p.runs || []).slice(-1)[0];
333
- if (detailRun && (p.stages || []).length > 0) {
328
+ if ((p.stages || []).length > 0) {
334
329
  html += _buildNodeChain(p.stages || [], detailRun, { compact: false, pipeline: p });
335
330
  }
336
331
  // Pipeline-level monitored resources (full view in detail)
@@ -452,26 +452,31 @@ function openWorkItemDetail(id) {
452
452
  var artPills = '';
453
453
  var pillStyle = 'display:inline-flex;align-items:center;gap:3px;padding:2px 8px;border-radius:10px;font-size:10px;cursor:pointer;background:var(--surface2);border:1px solid var(--border);color:var(--text)';
454
454
  // Output log pill removed — raw stream-json output is not human-readable
455
+ var artBackFn = "pushModalBack(function(){openWorkItemDetail('" + escHtml(item.id) + "')});";
455
456
  if (arts.branch) artPills += '<span style="' + pillStyle + ';cursor:default">🌿 ' + escHtml(arts.branch) + '</span> ';
456
- if (arts.plan) artPills += '<span onclick="planView(\'' + escHtml(arts.plan) + '\')" style="' + pillStyle + '">📋 Plan</span> ';
457
- if (arts.prd) artPills += '<span onclick="planView(\'' + escHtml(arts.prd) + '\')" style="' + pillStyle + '">📄 PRD</span> ';
458
- if (arts.sourcePlan) artPills += '<span onclick="planView(\'' + escHtml(arts.sourcePlan) + '\')" style="' + pillStyle + '">📋 Source Plan</span> ';
459
- if (arts.notes && arts.notes.length > 0) arts.notes.forEach(function(n) {
460
- var noteFile = (n && typeof n === 'object') ? (n.file || n) : String(n || '');
461
- if (noteFile.startsWith('kb:')) {
462
- var kbParts = noteFile.slice(3).split('/');
463
- var kbCat = kbParts[0];
464
- var kbFile = kbParts.slice(1).join('/');
465
- var kbLabel = kbFile.replace(/\.md$/, '').slice(0, 30);
466
- artPills += '<span onclick="kbOpenItem(\'' + escHtml(kbCat) + '\',\'' + escHtml(kbFile) + '\')" style="' + pillStyle + '">📚 ' + escHtml(kbLabel) + '</span> ';
467
- } else if (noteFile.startsWith('archive:')) {
468
- var archLabel = noteFile.slice(8).replace(/\.md$/, '').replace(/^\d{4}-\d{2}-\d{2}-/, '').slice(0, 30);
469
- artPills += '<span onclick="openInboxNote(\'' + escHtml(noteFile.slice(8)) + '\')" style="' + pillStyle + ';opacity:0.7">📄 ' + escHtml(archLabel) + ' <span style="font-size:8px">(archived)</span></span> ';
470
- } else {
471
- var noteLabel = noteFile.replace(/\.md$/, '').slice(0, 30);
472
- artPills += '<span onclick="openInboxNote(\'' + escHtml(noteFile) + '\')" style="' + pillStyle + '">📝 ' + escHtml(noteLabel) + '</span> ';
473
- }
474
- });
457
+ if (arts.plan) artPills += '<span onclick="' + artBackFn + 'planView(\'' + escHtml(arts.plan) + '\')" style="' + pillStyle + '">📋 Plan</span> ';
458
+ if (arts.prd) artPills += '<span onclick="' + artBackFn + 'planView(\'' + escHtml(arts.prd) + '\')" style="' + pillStyle + '">📄 PRD</span> ';
459
+ if (arts.sourcePlan) artPills += '<span onclick="' + artBackFn + 'planView(\'' + escHtml(arts.sourcePlan) + '\')" style="' + pillStyle + '">📋 Source Plan</span> ';
460
+ if (arts.notes && arts.notes.length > 0) {
461
+ var wiId = escHtml(item.id);
462
+ arts.notes.forEach(function(n) {
463
+ var noteFile = (n && typeof n === 'object') ? (n.file || n) : String(n || '');
464
+ var backFn = "pushModalBack(function(){openWorkItemDetail('" + wiId + "')});";
465
+ if (noteFile.startsWith('kb:')) {
466
+ var kbParts = noteFile.slice(3).split('/');
467
+ var kbCat = kbParts[0];
468
+ var kbFile = kbParts.slice(1).join('/');
469
+ var kbLabel = kbFile.replace(/\.md$/, '').slice(0, 30);
470
+ artPills += '<span onclick="' + backFn + 'kbOpenItem(\'' + escHtml(kbCat) + '\',\'' + escHtml(kbFile) + '\')" style="' + pillStyle + '">📚 ' + escHtml(kbLabel) + '</span> ';
471
+ } else if (noteFile.startsWith('archive:')) {
472
+ var archLabel = noteFile.slice(8).replace(/\.md$/, '').replace(/^\d{4}-\d{2}-\d{2}-/, '').slice(0, 30);
473
+ artPills += '<span onclick="' + backFn + 'openInboxNote(\'' + escHtml(noteFile.slice(8)) + '\')" style="' + pillStyle + ';opacity:0.7">📄 ' + escHtml(archLabel) + ' <span style="font-size:8px">(archived)</span></span> ';
474
+ } else {
475
+ var noteLabel = noteFile.replace(/\.md$/, '').slice(0, 30);
476
+ artPills += '<span onclick="' + backFn + 'openInboxNote(\'' + escHtml(noteFile) + '\')" style="' + pillStyle + '">📝 ' + escHtml(noteLabel) + '</span> ';
477
+ }
478
+ });
479
+ }
475
480
  if (arts.skills && arts.skills.length > 0) arts.skills.forEach(function(s) { artPills += '<span onclick="openSkill(\'' + escHtml(s) + '\',\'minions\',\'\')" style="' + pillStyle + '">⚙ ' + escHtml(s) + '</span> '; });
476
481
  if (artPills) html += field('Artifacts', '<div style="display:flex;flex-wrap:wrap;gap:4px">' + artPills + '</div>');
477
482
 
@@ -35,6 +35,25 @@ function _togglePinAndRefresh(key, source) {
35
35
  else if (source === 'kb') renderKnowledgeBase();
36
36
  }
37
37
 
38
+ // Modal navigation stack — enables back button when opening a modal from another modal
39
+ var _modalBackStack = [];
40
+ function pushModalBack(reopenFn) {
41
+ _modalBackStack.push(reopenFn);
42
+ var btn = document.getElementById('modal-back-btn');
43
+ if (btn) btn.style.display = '';
44
+ }
45
+ function modalGoBack() {
46
+ var fn = _modalBackStack.pop();
47
+ var btn = document.getElementById('modal-back-btn');
48
+ if (btn && _modalBackStack.length === 0) btn.style.display = 'none';
49
+ if (fn) fn();
50
+ }
51
+ function clearModalBackStack() {
52
+ _modalBackStack = [];
53
+ var btn = document.getElementById('modal-back-btn');
54
+ if (btn) btn.style.display = 'none';
55
+ }
56
+
38
57
  function updateModalPinBtn() {
39
58
  var btn = document.getElementById('modal-pin-btn');
40
59
  if (!btn) return;
@@ -98,6 +98,7 @@
98
98
  <div class="modal-header">
99
99
  <h3 id="modal-title">—</h3>
100
100
  <div class="modal-header-actions">
101
+ <button class="modal-copy" id="modal-back-btn" onclick="modalGoBack()" title="Back" style="display:none">&larr; Back</button>
101
102
  <button class="modal-copy" id="modal-edit-btn" onclick="modalToggleEdit()" title="Edit" style="display:none"><svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M11.013 1.427a1.75 1.75 0 012.474 0l1.086 1.086a1.75 1.75 0 010 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 01-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.61zM12.9 2.97L4.288 11.58l-.537 1.878 1.878-.537L14.242 4.31 12.9 2.97z"/></svg> Edit</button>
102
103
  <button class="modal-copy" id="modal-save-btn" onclick="modalSaveEdit()" title="Save" style="display:none;color:var(--green);border-color:var(--green)"><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> Save</button>
103
104
  <button class="modal-copy" id="modal-cancel-edit-btn" onclick="modalCancelEdit()" title="Cancel edit" style="display:none">Cancel</button>
@@ -194,7 +194,7 @@
194
194
  .pl-progress-label { display: flex; gap: 8px; align-items: center; margin-top: 4px; font-size: 10px; }
195
195
  .pl-node-chain { display: flex; align-items: flex-start; gap: 2px; overflow-x: auto; padding: 8px 0; }
196
196
  .pl-node { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; }
197
- .pl-node-box { padding: 4px 8px; border-radius: 6px; border: 2px solid var(--border); background: var(--surface2); text-align: center; font-size: 10px; transition: border-color 0.2s; white-space: nowrap; }
197
+ .pl-node-box { padding: 4px 8px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface2); text-align: center; font-size: 10px; transition: border-color 0.2s; white-space: nowrap; }
198
198
  .pl-node-box.complete { border-color: var(--green); background: rgba(63,185,80,0.08); }
199
199
  .pl-node-box.running { border-color: var(--blue); animation: plSegPulse 1.5s ease-in-out infinite; }
200
200
  .pl-node-box.failed { border-color: var(--red); background: rgba(248,81,73,0.08); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.686",
3
+ "version": "0.1.688",
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"
@@ -0,0 +1,42 @@
1
+ {
2
+ "id": "pr-review-fix-cycle",
3
+ "title": "PR Review & Fix Cycle",
4
+ "description": "Visualizes the default engine flow: review a PR, fix issues if rejected, re-review until approved and build passes.",
5
+ "trigger": { "manual": true },
6
+ "stages": [
7
+ {
8
+ "id": "review-pr",
9
+ "type": "task",
10
+ "taskType": "review",
11
+ "title": "Review PR",
12
+ "description": "Review the target PR for correctness, quality, and completeness. Provide actionable feedback if changes are needed."
13
+ },
14
+ {
15
+ "id": "check-review-status",
16
+ "type": "condition",
17
+ "title": "Approved?",
18
+ "check": "allBuildsGreen",
19
+ "action": "stop",
20
+ "onMet": "Merge",
21
+ "onUnmet": "Fix",
22
+ "dependsOn": ["review-pr"]
23
+ },
24
+ {
25
+ "id": "fix-feedback",
26
+ "type": "task",
27
+ "taskType": "fix",
28
+ "title": "Fix review feedback",
29
+ "description": "Address all review comments and push fixes to the PR branch.",
30
+ "dependsOn": ["check-review-status"]
31
+ },
32
+ {
33
+ "id": "re-review",
34
+ "type": "task",
35
+ "taskType": "review",
36
+ "title": "Re-review after fix",
37
+ "description": "Re-review the PR after fixes have been applied. Verify all prior feedback was addressed.",
38
+ "dependsOn": ["fix-feedback"]
39
+ }
40
+ ],
41
+ "stopWhen": "allBuildsGreen"
42
+ }