@yemi33/minions 0.1.990 → 0.1.991

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,12 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.990 (2026-04-15)
3
+ ## 0.1.991 (2026-04-15)
4
4
 
5
5
  ### Features
6
+ - fix doc-chat Clear chat not persisting session deletion to localStorage (#1102)
7
+ - remove DEFAULTS alias from timeout.js (#1101)
8
+ - Per-project workSources toggles in settings modal (#1096)
9
+ - support project-local playbook overrides (#1094)
6
10
  - gate review auto-dispatch on adoPollEnabled and evalLoop config flags (#1092)
7
11
  - remove DEFAULTS alias from timeout.js — standardize to ENGINE_DEFAULTS
8
12
  - complete watches management — new conditions, CC actions, PROJECTS fix (#1103)
@@ -19,10 +23,6 @@
19
23
  - ADO throttle detection, poll guards, and dashboard banner (#1051)
20
24
  - gate auto-fix conflict dispatch behind autoFixConflicts flag
21
25
  - add PR/build status CLI shim and agent guidance
22
- - make ADO poll frequency configurable and ungate reconcilePrs
23
- - update render-work-items.js output viewer to use renderAgentOutput
24
- - update detail-panel.js Output Log tab to use renderAgentOutput
25
- - Update command-center.js to capture tool inputs and use formatted summaries
26
26
 
27
27
  ### Fixes
28
28
  - updatePrAfterReview preserves fixedAt when writing minionsReview
@@ -135,7 +135,10 @@ function clearQaConversation() {
135
135
  var expandBar = document.getElementById('qa-expand-bar');
136
136
  if (wrap) wrap.style.display = 'none';
137
137
  if (expandBar) expandBar.style.display = 'none';
138
- if (_qaSessionKey) _qaSessions.delete(_qaSessionKey);
138
+ if (_qaSessionKey) {
139
+ _qaSessions.delete(_qaSessionKey);
140
+ _saveQaSessions();
141
+ }
139
142
  }
140
143
 
141
144
  function modalSend() {
@@ -68,6 +68,18 @@ async function openSettings() {
68
68
  settingsField('Ignored Comment Authors', 'set-ignoredCommentAuthors', (e.ignoredCommentAuthors || []).join(', '), '', 'Comma-separated usernames — comments auto-closed, never trigger fixes') +
69
69
  '</div>' +
70
70
 
71
+ '<h3 style="font-size:13px;color:var(--blue);margin-bottom:8px">Projects</h3>' +
72
+ '<div style="display:flex;flex-direction:column;gap:12px;margin-bottom:16px">' +
73
+ (data.projects || []).map(function(p) {
74
+ return '<div style="border:1px solid var(--border);border-radius:6px;padding:10px 12px">' +
75
+ '<div style="font-size:12px;font-weight:600;margin-bottom:8px">' + escHtml(p.name) + '</div>' +
76
+ '<div style="display:flex;flex-direction:column;gap:6px">' +
77
+ settingsToggle('Discover from PRs', 'set-ws-prs-' + p.name, p.workSources.pullRequests.enabled, 'Auto-discover work from open pull requests') +
78
+ settingsToggle('Discover from Work Items', 'set-ws-wi-' + p.name, p.workSources.workItems.enabled, 'Auto-discover work from ADO/GitHub work items') +
79
+ '</div></div>';
80
+ }).join('') +
81
+ '</div>' +
82
+
71
83
  '<h3 style="font-size:13px;color:var(--blue);margin-bottom:8px">Max Turns by Task Type</h3>' +
72
84
  '<div style="font-size:10px;color:var(--muted);margin-bottom:6px">How many tool-use turns each task type gets before forced stop. Blank = built-in default.</div>' +
73
85
  '<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-bottom:16px">' +
@@ -293,10 +305,20 @@ async function saveSettings() {
293
305
  agentsPayload[id][field] = el.value;
294
306
  });
295
307
 
308
+ const projectsPayload = (data.projects || []).map(function(p) {
309
+ return {
310
+ name: p.name,
311
+ workSources: {
312
+ pullRequests: { enabled: document.getElementById('set-ws-prs-' + p.name)?.checked ?? true },
313
+ workItems: { enabled: document.getElementById('set-ws-wi-' + p.name)?.checked ?? true }
314
+ }
315
+ };
316
+ });
317
+
296
318
  // Save config
297
319
  const res = await fetch('/api/settings', {
298
320
  method: 'POST', headers: { 'Content-Type': 'application/json' },
299
- body: JSON.stringify({ engine: enginePayload, claude: claudePayload, agents: agentsPayload, teams: teamsPayload })
321
+ body: JSON.stringify({ engine: enginePayload, claude: claudePayload, agents: agentsPayload, teams: teamsPayload, projects: projectsPayload })
300
322
  });
301
323
  const result = await res.json();
302
324
  if (!res.ok) throw new Error(result.error);
package/dashboard.js CHANGED
@@ -3865,6 +3865,13 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3865
3865
  claude: { ...shared.DEFAULT_CLAUDE, ...(config.claude || {}) },
3866
3866
  agents: config.agents || {},
3867
3867
  teams: { ...shared.ENGINE_DEFAULTS.teams, ...(config.teams || {}) },
3868
+ projects: (config.projects || []).map(p => ({
3869
+ name: p.name,
3870
+ workSources: {
3871
+ pullRequests: { enabled: p.workSources?.pullRequests?.enabled !== false, cooldownMinutes: p.workSources?.pullRequests?.cooldownMinutes ?? 30 },
3872
+ workItems: { enabled: p.workSources?.workItems?.enabled !== false, cooldownMinutes: p.workSources?.workItems?.cooldownMinutes ?? 0 }
3873
+ }
3874
+ })),
3868
3875
  routing,
3869
3876
  });
3870
3877
  } catch (e) { return jsonReply(res, 500, { error: e.message }); }
@@ -3977,6 +3984,25 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3977
3984
  teams._resetAdapter();
3978
3985
  }
3979
3986
 
3987
+ if (body.projects && Array.isArray(body.projects)) {
3988
+ if (!config.projects) config.projects = [];
3989
+ for (const update of body.projects) {
3990
+ const proj = config.projects.find(p => p.name === update.name);
3991
+ if (!proj) continue;
3992
+ if (!proj.workSources) proj.workSources = {};
3993
+ if (update.workSources?.pullRequests !== undefined) {
3994
+ if (!proj.workSources.pullRequests) proj.workSources.pullRequests = { enabled: true, cooldownMinutes: 30 };
3995
+ if (update.workSources.pullRequests.enabled !== undefined)
3996
+ proj.workSources.pullRequests.enabled = !!update.workSources.pullRequests.enabled;
3997
+ }
3998
+ if (update.workSources?.workItems !== undefined) {
3999
+ if (!proj.workSources.workItems) proj.workSources.workItems = { enabled: true, cooldownMinutes: 0 };
4000
+ if (update.workSources.workItems.enabled !== undefined)
4001
+ proj.workSources.workItems.enabled = !!update.workSources.workItems.enabled;
4002
+ }
4003
+ }
4004
+ }
4005
+
3980
4006
  safeWrite(configPath, config);
3981
4007
  // Refresh in-memory CONFIG so subsequent reads see the update
3982
4008
  reloadConfig();
@@ -4752,7 +4778,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
4752
4778
 
4753
4779
  // Settings
4754
4780
  { method: 'GET', path: '/api/settings', desc: 'Return current engine + claude + routing config', handler: handleSettingsRead },
4755
- { method: 'POST', path: '/api/settings', desc: 'Update engine + claude + agent + teams config', params: 'engine?, claude?, agents?, teams?', handler: handleSettingsUpdate },
4781
+ { method: 'POST', path: '/api/settings', desc: 'Update engine + claude + agent + teams + projects config', params: 'engine?, claude?, agents?, teams?, projects?', handler: handleSettingsUpdate },
4756
4782
  { method: 'POST', path: '/api/settings/routing', desc: 'Update routing.md', params: 'content', handler: handleSettingsRouting },
4757
4783
  { method: 'POST', path: '/api/settings/reset', desc: 'Reset engine + claude + agent settings to defaults', handler: handleSettingsReset },
4758
4784
 
@@ -283,12 +283,14 @@ function resolvePlaybookPath(projectName, playbookType) {
283
283
  const localPath = path.join(MINIONS_DIR, 'projects', projectName, 'playbooks', `${playbookType}.md`);
284
284
  try {
285
285
  fs.accessSync(localPath, fs.constants.R_OK);
286
+ log('info', `Using project-local playbook: projects/${projectName}/playbooks/${playbookType}.md`);
286
287
  return localPath;
287
288
  } catch { /* no local override — fall through to global */ }
288
289
  }
289
290
  return path.join(PLAYBOOKS_DIR, `${playbookType}.md`);
290
291
  }
291
292
 
293
+
292
294
  // ─── Playbook Renderer ──────────────────────────────────────────────────────
293
295
 
294
296
  function renderPlaybook(type, vars) {
@@ -589,8 +591,8 @@ function buildPrDispatch(agentId, config, project, pr, type, extraVars, taskLabe
589
591
  }
590
592
 
591
593
  module.exports = {
592
- renderPlaybook,
593
594
  resolvePlaybookPath,
595
+ renderPlaybook,
594
596
  validatePlaybookVars,
595
597
  PLAYBOOK_REQUIRED_VARS,
596
598
  PLAYBOOK_OPTIONAL_VARS,
package/engine/watches.js CHANGED
@@ -11,7 +11,7 @@
11
11
  const path = require('path');
12
12
  const shared = require('./shared');
13
13
  const { safeJson, mutateJsonFileLocked, ts, uid, log, writeToInbox,
14
- WATCH_STATUS, WATCH_TARGET_TYPE, WATCH_CONDITION } = shared;
14
+ WATCH_STATUS, WATCH_TARGET_TYPE, WATCH_CONDITION, WATCH_ABSOLUTE_CONDITIONS } = shared;
15
15
 
16
16
  // Dynamic path — respects MINIONS_TEST_DIR for test isolation
17
17
  function _watchesPath() { return path.join(shared.MINIONS_DIR, 'engine', 'watches.json'); }
@@ -248,10 +248,13 @@ function checkWatches(config, state) {
248
248
  log('info', `Watch triggered: ${watch.id} — ${result.message}`);
249
249
 
250
250
  // Expire when stopAfter > 0 and trigger count reaches the limit.
251
- // stopAfter: 0 means "run forever" for all condition types.
252
- if (watch.stopAfter > 0 && watch.triggerCount >= watch.stopAfter) {
251
+ // Absolute conditions (merged, build-pass, etc.) auto-expire on first trigger
252
+ // when stopAfter=0 fire-once semantics. Change-based conditions run forever.
253
+ const isAbsolute = WATCH_ABSOLUTE_CONDITIONS.has(watch.condition);
254
+ if ((watch.stopAfter > 0 && watch.triggerCount >= watch.stopAfter) ||
255
+ (watch.stopAfter === 0 && isAbsolute)) {
253
256
  watch.status = WATCH_STATUS.EXPIRED;
254
- log('info', `Watch expired (stopAfter limit reached): ${watch.id}`);
257
+ log('info', `Watch expired (${isAbsolute && watch.stopAfter === 0 ? 'absolute condition fire-once' : 'stopAfter limit reached'}): ${watch.id}`);
255
258
  }
256
259
  } else if (watch.onNotMet === 'notify' && watch.owner) {
257
260
  // Queue per-poll notification when condition is not yet met — unique key per poll
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.990",
3
+ "version": "0.1.991",
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"