@yemi33/minions 0.1.593 → 0.1.595

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,11 +1,13 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.593 (2026-04-08)
3
+ ## 0.1.595 (2026-04-08)
4
4
 
5
5
  ### Features
6
6
  - show worktree count and engine quick stats on engine page
7
7
 
8
8
  ### Fixes
9
+ - bump fix task max-turns from 50 to 75
10
+ - Pipeline UI show monitored resources in pipeline card (closes #523) (#542)
9
11
  - move 'When to Stop' after Build/Test in implement playbook
10
12
  - per-type maxTurns bypassed when config has default maxTurns=100
11
13
 
@@ -5,6 +5,62 @@ let _pipelinePollId = null;
5
5
  let _pipelinePollInterval = null;
6
6
  function _stopPipelinePoll() { if (_pipelinePollInterval) { clearInterval(_pipelinePollInterval); _pipelinePollInterval = null; } _pipelinePollId = null; }
7
7
 
8
+ /**
9
+ * Collect all monitoredResources from a pipeline (pipeline-level + all stages).
10
+ * Returns a deduplicated array of resource objects.
11
+ */
12
+ function _collectMonitoredResources(pipeline) {
13
+ var seen = new Set();
14
+ var result = [];
15
+ function add(r) {
16
+ var key = typeof r === 'string' ? r : (r.url || r.label || JSON.stringify(r));
17
+ if (seen.has(key)) return;
18
+ seen.add(key);
19
+ result.push(typeof r === 'string' ? { label: r, url: r } : r);
20
+ }
21
+ (pipeline.monitoredResources || []).forEach(add);
22
+ (pipeline.stages || []).forEach(function(s) { (s.monitoredResources || []).forEach(add); });
23
+ return result;
24
+ }
25
+
26
+ /**
27
+ * Render monitored resources as compact pills on a pipeline card or stage detail.
28
+ * Supports both string resources (URLs/IDs) and objects with {type, label, url}.
29
+ * @param {Array} resources - array of resource strings or {type?, label, url?} objects
30
+ * @param {Object} [options] - { compact: true } limits display and shows "+N more"
31
+ * @returns {string} HTML string
32
+ */
33
+ function _renderMonitoredResources(resources, options) {
34
+ if (!resources || resources.length === 0) return '';
35
+ var compact = options && options.compact;
36
+ var maxShow = compact ? 4 : resources.length;
37
+ var shown = resources.slice(0, maxShow);
38
+ var overflow = resources.length - maxShow;
39
+
40
+ var iconMap = { pr: '🔀', workitem: '⚙', url: '🔗', issue: '🐛' };
41
+ var pillStyle = 'display:inline-flex;align-items:center;gap:2px;padding:1px 6px;border-radius:10px;font-size:10px;text-decoration:none;' +
42
+ 'color:var(--text);background:color-mix(in srgb, var(--muted) 10%, transparent);border:1px solid color-mix(in srgb, var(--muted) 20%, transparent);max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
43
+
44
+ var pills = shown.map(function(r) {
45
+ var res = typeof r === 'string' ? { label: r, url: r.startsWith('http') ? r : '' } : r;
46
+ var icon = iconMap[res.type] || (res.url ? '🔗' : '📌');
47
+ var label = res.label || res.url || '(resource)';
48
+ // Truncate label for compact view
49
+ var displayLabel = compact && label.length > 28 ? label.slice(0, 26) + '…' : label;
50
+ if (res.url) {
51
+ return '<a href="' + escHtml(res.url) + '" target="_blank" rel="noopener" style="' + pillStyle + ';cursor:pointer" onclick="event.stopPropagation()" title="' + escHtml(label) + '">' + icon + ' ' + escHtml(displayLabel) + '</a>';
52
+ }
53
+ return '<span style="' + pillStyle + ';cursor:default" title="' + escHtml(label) + '">' + icon + ' ' + escHtml(displayLabel) + '</span>';
54
+ });
55
+
56
+ if (overflow > 0) {
57
+ pills.push('<span style="' + pillStyle + ';cursor:default;opacity:0.7" title="' + overflow + ' more resource' + (overflow !== 1 ? 's' : '') + '">+' + overflow + ' more</span>');
58
+ }
59
+
60
+ var heading = compact ? '' : '<span style="font-size:10px;color:var(--muted);margin-right:4px">Monitoring:</span>';
61
+ return '<div style="margin-top:4px;display:flex;flex-wrap:wrap;gap:3px;align-items:center">' + heading + pills.join('') + '</div>';
62
+ }
63
+
8
64
  /**
9
65
  * Render clickable artifact links for a pipeline stage.
10
66
  * Each artifact type gets an icon and navigates to the relevant detail view.
@@ -154,6 +210,10 @@ function renderPipelines(pipelines) {
154
210
  progressHtml = _buildProgressBar(p.stages || [], displayRun);
155
211
  }
156
212
 
213
+ // Monitored resources (pipeline-level + stage-level, compact on card)
214
+ var allResources = _collectMonitoredResources(p);
215
+ var resourcesHtml = _renderMonitoredResources(allResources, { compact: true });
216
+
157
217
  return '<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 16px;margin-bottom:8px;cursor:pointer" onclick="openPipelineDetail(\'' + escHtml(p.id) + '\')">' +
158
218
  '<div style="display:flex;justify-content:space-between;align-items:center">' +
159
219
  '<strong style="font-size:13px">' + escHtml(p.title) + '</strong>' +
@@ -164,6 +224,7 @@ function renderPipelines(pipelines) {
164
224
  '</div>' +
165
225
  '</div>' +
166
226
  '<div style="margin-top:6px;display:flex;gap:4px;align-items:center;flex-wrap:wrap">' + stageFlow + '</div>' +
227
+ resourcesHtml +
167
228
  progressHtml +
168
229
  '</div>';
169
230
  }).join('');
@@ -196,6 +257,15 @@ function openPipelineDetail(id) {
196
257
  if (detailRun && (p.stages || []).length > 0) {
197
258
  html += _buildProgressBar(p.stages || [], detailRun, { height: '8px', detailLabel: true });
198
259
  }
260
+ // Pipeline-level monitored resources (full view in detail)
261
+ var pipelineResources = _collectMonitoredResources(p);
262
+ if (pipelineResources.length > 0) {
263
+ html += '<div style="border:1px solid color-mix(in srgb, var(--blue) 20%, transparent);border-radius:6px;padding:6px 10px;background:color-mix(in srgb, var(--blue) 4%, transparent)">' +
264
+ '<span style="font-size:10px;font-weight:600;color:var(--blue)">📡 Monitored Resources</span>' +
265
+ _renderMonitoredResources(pipelineResources) +
266
+ '</div>';
267
+ }
268
+
199
269
  html += '<h4 style="font-size:12px;color:var(--blue);margin:0">Stages</h4>';
200
270
  (p.stages || []).forEach(function(s, i) {
201
271
  var stageRun = activeRun?.stages?.[s.id] || {};
@@ -209,6 +279,7 @@ function openPipelineDetail(id) {
209
279
  '<span style="color:' + statusColor + ';font-size:10px;font-weight:600">' + stageStatus.toUpperCase() + '</span>' +
210
280
  '</div>' +
211
281
  '<div style="font-size:10px;color:var(--muted);margin-top:4px">Type: ' + escHtml(s.type) + ' | Depends on: ' + escHtml(deps) + (s.agent ? ' | Agent: ' + escHtml(s.agent) : '') + '</div>' +
282
+ _renderMonitoredResources(s.monitoredResources || []) +
212
283
  _renderArtifactLinks(stageRun.artifacts) +
213
284
  (stageRun.output ? '<div style="margin-top:6px;font-size:11px;max-height:150px;overflow-y:auto">' + renderMd(stageRun.output.slice(0, 500)) + '</div>' : '') +
214
285
  (stageStatus === 'waiting-human' ? '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;color:var(--green);border-color:var(--green);margin-top:6px" onclick="_continuePipeline(\'' + escHtml(id) + '\',\'' + escHtml(s.id) + '\',this)">Continue</button>' : '') +
package/dashboard.js CHANGED
@@ -3952,17 +3952,18 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3952
3952
  const result = pipelines.map(p => ({ ...p, runs: (runs[p.id] || []).slice(-5) }));
3953
3953
  return jsonReply(res, 200, result);
3954
3954
  }},
3955
- { method: 'POST', path: '/api/pipelines', desc: 'Create a pipeline', params: 'id, title, stages[], trigger?', handler: async (req, res) => {
3955
+ { method: 'POST', path: '/api/pipelines', desc: 'Create a pipeline', params: 'id, title, stages[], trigger?, monitoredResources?', handler: async (req, res) => {
3956
3956
  const body = await readBody(req);
3957
3957
  if (!body.id || !body.title || !body.stages) return jsonReply(res, 400, { error: 'id, title, and stages required' });
3958
3958
  const { savePipeline, getPipeline } = require('./engine/pipeline');
3959
3959
  if (getPipeline(body.id)) return jsonReply(res, 409, { error: 'Pipeline already exists' });
3960
3960
  const pipeline = { id: body.id, title: body.title, stages: body.stages, trigger: body.trigger || {}, enabled: body.enabled !== false };
3961
+ if (Array.isArray(body.monitoredResources) && body.monitoredResources.length > 0) pipeline.monitoredResources = body.monitoredResources;
3961
3962
  savePipeline(pipeline);
3962
3963
  invalidateStatusCache();
3963
3964
  return jsonReply(res, 200, { ok: true, id: pipeline.id });
3964
3965
  }},
3965
- { method: 'POST', path: '/api/pipelines/update', desc: 'Update a pipeline', params: 'id, title?, stages?, trigger?, enabled?', handler: async (req, res) => {
3966
+ { method: 'POST', path: '/api/pipelines/update', desc: 'Update a pipeline', params: 'id, title?, stages?, trigger?, enabled?, monitoredResources?', handler: async (req, res) => {
3966
3967
  const body = await readBody(req);
3967
3968
  if (!body.id) return jsonReply(res, 400, { error: 'id required' });
3968
3969
  const { getPipeline, savePipeline } = require('./engine/pipeline');
@@ -3972,6 +3973,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3972
3973
  if (body.stages !== undefined) pipeline.stages = body.stages;
3973
3974
  if (body.trigger !== undefined) pipeline.trigger = body.trigger;
3974
3975
  if (body.enabled !== undefined) pipeline.enabled = body.enabled;
3976
+ if (body.monitoredResources !== undefined) pipeline.monitoredResources = body.monitoredResources;
3975
3977
  savePipeline(pipeline);
3976
3978
  invalidateStatusCache();
3977
3979
  return jsonReply(res, 200, { ok: true });
package/engine.js CHANGED
@@ -151,7 +151,7 @@ const _MAX_TURNS_BY_TYPE = {
151
151
  [WORK_TYPE.EXPLORE]: 30, [WORK_TYPE.ASK]: 20, [WORK_TYPE.REVIEW]: 30,
152
152
  [WORK_TYPE.DECOMPOSE]: 15, [WORK_TYPE.PLAN]: 30, [WORK_TYPE.PLAN_TO_PRD]: 20,
153
153
  [WORK_TYPE.MEETING]: 30,
154
- [WORK_TYPE.IMPLEMENT]: 75, [WORK_TYPE.IMPLEMENT_LARGE]: 75, [WORK_TYPE.FIX]: 50,
154
+ [WORK_TYPE.IMPLEMENT]: 75, [WORK_TYPE.IMPLEMENT_LARGE]: 75, [WORK_TYPE.FIX]: 75,
155
155
  [WORK_TYPE.TEST]: 50, [WORK_TYPE.VERIFY]: 100, [WORK_TYPE.DOCS]: 30,
156
156
  };
157
157
  function _maxTurnsForType(type, engineConfig) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.593",
3
+ "version": "0.1.595",
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"