@yemi33/minions 0.1.1772 → 0.1.1773

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,11 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.1772 (2026-05-07)
3
+ ## 0.1.1773 (2026-05-07)
4
4
 
5
5
  ### Features
6
+ - fix doc chat sticky scroll (#2176)
7
+ - fix live output route and CLI docs (#2172)
8
+ - make API project resolution strict (#2169)
6
9
  - fix copilot cc resume context (#2166)
7
10
 
8
11
  ## 0.1.1771 (2026-05-07)
package/README.md CHANGED
@@ -127,21 +127,31 @@ minions work "Explore the codebase and document the architecture"
127
127
  | `minions init` | Bootstrap `~/.minions/` with default agents and config |
128
128
  | `minions update` | Update to latest version (npm update + apply) |
129
129
  | `minions version` | Show installed vs package version |
130
+ | `minions doctor` | Check prerequisites and runtime health |
130
131
  | `minions scan [dir] [depth]` | Scan for git repos and multi-select to add (default: ~, depth 3) |
131
132
  | `minions add <dir>` | Link a single project (auto-detects settings from git, prompts to confirm) |
132
133
  | `minions remove <dir-or-name> [--keep-data \| --purge --force]` | Unlink a project: cancels pending work items, drains dispatch + kills active agents, cleans worktrees, disables linked schedules, archives `projects/<name>/` to `projects/.archived/<name>-YYYYMMDD/`. Use `--keep-data` to leave the data dir in place, or `--purge --force` to delete it. |
133
134
  | `minions list` | List all linked projects with descriptions |
135
+ | `minions restart` | Start engine and dashboard together (recommended after reboot) |
134
136
  | `minions start` | Start engine daemon (ticks every 60s, auto-syncs MCP servers) |
135
137
  | `minions stop` | Stop the engine |
136
138
  | `minions status` | Show agents, projects, dispatch queue, quality metrics |
137
139
  | `minions pause` / `resume` | Pause/resume dispatching |
138
140
  | `minions dispatch` | Force a dispatch cycle |
139
141
  | `minions discover` | Dry-run work discovery |
142
+ | `minions queue` | Show dispatch queue (pending, active, completed) |
143
+ | `minions sources` | Show work source status per project |
140
144
  | `minions work <title> [opts]` | Add to central work queue |
141
145
  | `minions spawn <agent> <prompt>` | Manually spawn an agent |
142
146
  | `minions plan <file\|text> [proj]` | Run a plan |
147
+ | `minions kill` | Kill all active agents and reset their dispatches to pending |
148
+ | `minions complete <dispatch-id>` | Manually mark a dispatch as completed |
149
+ | `minions config set-cli <R> [--model M]` | Persist the default runtime/model without starting the engine |
150
+ | `minions mcp-sync` | Sync MCP servers from `~/.claude.json` |
143
151
  | `minions cleanup` | Run cleanup manually (temp files, worktrees, zombies) |
144
152
  | `minions dash` | Open dashboard (starts if not already running, port 7331) |
153
+ | `minions nuke --confirm` | Factory reset runtime state and reset config to defaults |
154
+ | `minions uninstall --confirm` | Remove Minions state and uninstall the npm package |
145
155
 
146
156
  You can also run scripts directly: `node ~/.minions/engine.js start`, `node ~/.minions/dashboard.js`, etc.
147
157
 
package/bin/minions.js CHANGED
@@ -8,18 +8,29 @@
8
8
  * minions add <project-dir> Link a project (interactive)
9
9
  * minions remove <project-dir> Unlink a project
10
10
  * minions list List linked projects
11
+ * minions update Update to latest version
12
+ * minions version Show installed and package versions
13
+ * minions doctor Check prerequisites and runtime health
14
+ * minions restart Start engine + dashboard
11
15
  * minions start Start the engine
12
16
  * minions stop Stop the engine
13
17
  * minions status Show engine status
14
18
  * minions pause / resume Pause/resume dispatching
19
+ * minions queue Show dispatch queue
20
+ * minions sources Show work source status
15
21
  * minions dash Start the dashboard
16
22
  * minions work <title> [opts-json] Add a work item
17
23
  * minions spawn <agent> <prompt> Manually spawn an agent
18
24
  * minions dispatch Force a dispatch cycle
19
25
  * minions discover Dry-run work discovery
20
26
  * minions cleanup Run cleanup manually
27
+ * minions kill Kill active agents and reset to pending
28
+ * minions complete <dispatch-id> Mark a dispatch completed
29
+ * minions config set-cli <R> [--model M] Persist default runtime/model
21
30
  * minions plan <file|text> [proj] Run a plan
22
- * minions version Show installed and package versions
31
+ * minions mcp-sync Sync MCP servers from ~/.claude.json
32
+ * minions nuke --confirm Factory reset runtime state/config
33
+ * minions uninstall --confirm Remove Minions and uninstall package
23
34
  */
24
35
 
25
36
  const fs = require('fs');
@@ -642,6 +653,9 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
642
653
  minions plan <file|text> [proj] Run a plan
643
654
  minions kill Kill all active agents and reset to pending
644
655
  minions complete <dispatch-id> Manually mark a dispatch as completed
656
+ minions config set-cli <R> [--model M]
657
+ Persist default runtime/model without starting
658
+ minions mcp-sync Sync MCP servers from ~/.claude.json
645
659
  minions cleanup Clean temp files, worktrees, zombies
646
660
  minions nuke --confirm Factory reset (delete state, reset config to defaults)
647
661
  minions uninstall --confirm Remove everything + uninstall npm package
@@ -30,6 +30,32 @@ const QA_QUEUE_CAP = 10; // max queued messages
30
30
  const QA_STREAM_STALL_MS = 6 * 60 * 1000; // allow the full doc-chat timeout before treating heartbeat-only streams as stalled
31
31
  let _qaSessionKey = ''; // key for current conversation (title or filePath)
32
32
 
33
+ const QA_STICKY_BOTTOM_PX = 80;
34
+ let _qaThreadShouldFollow = true;
35
+
36
+ function _qaIsNearThreadBottom(thread) {
37
+ if (!thread) return true;
38
+ return thread.scrollHeight - thread.scrollTop - thread.clientHeight <= QA_STICKY_BOTTOM_PX;
39
+ }
40
+
41
+ function _qaShouldFollowThread(thread) {
42
+ return _qaThreadShouldFollow && _qaIsNearThreadBottom(thread);
43
+ }
44
+
45
+ function _qaSetThreadFollowFromScroll(thread) {
46
+ _qaThreadShouldFollow = _qaIsNearThreadBottom(thread);
47
+ }
48
+
49
+ function _qaScrollThreadToBottom(thread) {
50
+ if (!thread) return;
51
+ thread.scrollTop = thread.scrollHeight;
52
+ _qaThreadShouldFollow = true;
53
+ }
54
+
55
+ function _qaMaybeScrollThreadToBottom(thread, shouldFollow) {
56
+ if (shouldFollow) _qaScrollThreadToBottom(thread);
57
+ }
58
+
33
59
  // Insert html at the bottom of the thread but above any pending "Queued: ..."
34
60
  // strips, so queued messages always remain visually below the active progress
35
61
  // UX / answer / errors.
@@ -47,7 +73,7 @@ function _renderQaUserMessage(thread, message, selection) {
47
73
  }
48
74
  qHtml += '</div>';
49
75
  _qaInsertBeforeQueued(thread, qHtml);
50
- thread.scrollTop = thread.scrollHeight;
76
+ _qaScrollThreadToBottom(thread);
51
77
  _showThreadWrap();
52
78
  }
53
79
  const _qaSessions = new Map(); // persist conversations across modal open/close {key → {history, threadHtml}}
@@ -91,6 +117,10 @@ function _qaThreadEl() {
91
117
  return document.getElementById('modal-qa-thread');
92
118
  }
93
119
 
120
+ document.addEventListener('scroll', function(e) {
121
+ if (e.target && e.target.id === 'modal-qa-thread') _qaSetThreadFollowFromScroll(e.target);
122
+ }, true);
123
+
94
124
  function _qaThreadHtml() {
95
125
  return (_qaThreadEl() || {}).innerHTML || '';
96
126
  }
@@ -308,8 +338,9 @@ function _qaMutateThreadHtml(key, mutate) {
308
338
  const wasCollapsed = _qaIsThreadCollapsed();
309
339
  const thread = _qaThreadEl();
310
340
  if (thread) {
341
+ const shouldFollow = _qaShouldFollowThread(thread);
311
342
  thread.innerHTML = html;
312
- thread.scrollTop = thread.scrollHeight;
343
+ _qaMaybeScrollThreadToBottom(thread, shouldFollow);
313
344
  }
314
345
  if (wasCollapsed) _setQaThreadCollapsed(true);
315
346
  else _showThreadWrap();
@@ -386,7 +417,7 @@ function _initQaSession() {
386
417
  _showThreadWrap();
387
418
  requestAnimationFrame(function() {
388
419
  var thread = document.getElementById('modal-qa-thread');
389
- if (thread) thread.scrollTop = thread.scrollHeight;
420
+ _qaScrollThreadToBottom(thread);
390
421
  });
391
422
  if (_qaQueue.length > 0 && !_qaProcessing) {
392
423
  setTimeout(_qaResumeQueuedMessages, 0);
@@ -394,6 +425,7 @@ function _initQaSession() {
394
425
  } else {
395
426
  _qaHistory = [];
396
427
  document.getElementById('modal-qa-thread').innerHTML = '';
428
+ _qaThreadShouldFollow = true;
397
429
  var wrap = document.getElementById('modal-qa-thread-wrap');
398
430
  var expandBar = document.getElementById('qa-expand-bar');
399
431
  if (wrap) wrap.style.display = 'none';
@@ -410,6 +442,7 @@ function clearQaConversation() {
410
442
  _qaProcessing = false;
411
443
  _qaAbortController = null;
412
444
  document.getElementById('modal-qa-thread').innerHTML = '';
445
+ _qaThreadShouldFollow = true;
413
446
  var wrap = document.getElementById('modal-qa-thread-wrap');
414
447
  var expandBar = document.getElementById('qa-expand-bar');
415
448
  if (wrap) wrap.style.display = 'none';
@@ -455,7 +488,7 @@ function modalSend() {
455
488
  }
456
489
  _qaQueue.push({ message, selection });
457
490
  thread.insertAdjacentHTML('beforeend', _qaBuildQueuedHtml(message));
458
- thread.scrollTop = thread.scrollHeight;
491
+ _qaScrollThreadToBottom(thread);
459
492
  _showThreadWrap();
460
493
  _qaSaveActiveSessionState();
461
494
  return;
package/dashboard.js CHANGED
@@ -281,12 +281,23 @@ function createWorkItemWithDedup(wiPath, item, options = {}) {
281
281
  return result || { created: false, item: null };
282
282
  }
283
283
 
284
+ function formatUnknownProjectError(projectName, projects = []) {
285
+ const known = projects.map(p => p.name).filter(Boolean).join(', ') || '(none configured)';
286
+ return `Project "${projectName}" not found. Known projects: ${known}`;
287
+ }
288
+
289
+ function findProjectByName(projects, projectName) {
290
+ const name = String(projectName || '').trim().toLowerCase();
291
+ if (!name) return null;
292
+ return projects.find(p => p.name?.toLowerCase() === name) || null;
293
+ }
294
+
284
295
  function resolveWorkItemsCreateTarget(projectName, projects = PROJECTS) {
285
296
  const project = String(projectName || '').trim();
286
297
  let targetProject = null;
287
298
  if (project) {
288
- targetProject = projects.find(p => p.name === project) || (projects.length > 0 ? projects[0] : null);
289
- if (!targetProject) return { error: 'No projects configured' };
299
+ targetProject = findProjectByName(projects, project);
300
+ if (!targetProject) return { error: formatUnknownProjectError(project, projects) };
290
301
  } else if (projects.length === 1) {
291
302
  targetProject = projects[0];
292
303
  }
@@ -326,7 +337,13 @@ function linkPullRequestForTracking({ url, title, project: projectName, autoObse
326
337
  throw err;
327
338
  }
328
339
  const projects = shared.getProjects(config);
329
- const targetProject = projectName ? projects.find(p => p.name?.toLowerCase() === projectName.toLowerCase()) : (projects[0] || null);
340
+ const explicitProjectName = String(projectName || '').trim();
341
+ const targetProject = explicitProjectName ? findProjectByName(projects, explicitProjectName) : (projects[0] || null);
342
+ if (explicitProjectName && !targetProject) {
343
+ const err = new Error(formatUnknownProjectError(explicitProjectName, projects));
344
+ err.statusCode = 400;
345
+ throw err;
346
+ }
330
347
  const prPath = targetProject ? shared.projectPrPath(targetProject) : path.join(MINIONS_DIR, 'pull-requests.json');
331
348
 
332
349
  const prNumMatch = url.match(/\/pull\/(\d+)|pullrequest\/(\d+)/);
@@ -7121,6 +7138,13 @@ What would you like to discuss or change? When you're happy, say "approve" and I
7121
7138
  if (!url) return jsonReply(res, 400, { error: 'url required' });
7122
7139
 
7123
7140
  reloadConfig();
7141
+ const explicitProjectName = String(body.project || '').trim();
7142
+ if (explicitProjectName) {
7143
+ const projects = shared.getProjects(CONFIG);
7144
+ if (!findProjectByName(projects, explicitProjectName)) {
7145
+ return jsonReply(res, 400, { error: formatUnknownProjectError(explicitProjectName, projects) }, req);
7146
+ }
7147
+ }
7124
7148
  const adoTarget = parseAdoPrMetadataTarget(url);
7125
7149
  let initialPrData = null;
7126
7150
  if (adoTarget) {
@@ -7130,7 +7154,13 @@ What would you like to discuss or change? When you're happy, say "approve" and I
7130
7154
  shared.log('warn', `ADO PR link metadata fetch failed for ${url}: ${e.message}`);
7131
7155
  }
7132
7156
  }
7133
- const { id: prId, prPath, prNum, created, linked } = linkPullRequestForTracking(body, CONFIG, { metadata: initialPrData });
7157
+ let linkResult;
7158
+ try {
7159
+ linkResult = linkPullRequestForTracking(body, CONFIG, { metadata: initialPrData });
7160
+ } catch (e) {
7161
+ return jsonReply(res, e.statusCode || 400, { error: e.message }, req);
7162
+ }
7163
+ const { id: prId, prPath, prNum, created, linked } = linkResult;
7134
7164
  invalidateStatusCache();
7135
7165
  jsonReply(res, 200, { ok: true, id: prId, created, linked });
7136
7166
 
@@ -7268,6 +7298,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
7268
7298
  { method: 'POST', path: /^\/api\/agent\/([\w-]+)\/kill$/, desc: 'Kill a running agent: stop process, clear dispatch, reset work items to pending', handler: handleAgentKill },
7269
7299
  { method: 'GET', path: /^\/api\/agent\/([\w-]+)\/live-stream(?:\?.*)?$/, desc: 'SSE real-time live output streaming', handler: handleAgentLiveStream },
7270
7300
  { method: 'GET', path: /^\/api\/agent\/([\w-]+)\/live(?:\?.*)?$/, desc: 'Tail live output for a working agent', params: 'tail? (bytes, default 8192)', handler: handleAgentLive },
7301
+ { method: 'GET', path: /^\/api\/agent\/([\w-]+)\/live-output(?:\?.*)?$/, desc: 'Tail live output for a working agent (alias for /live)', params: 'tail? (bytes, default 8192)', handler: handleAgentLive },
7271
7302
  { method: 'GET', path: /^\/api\/agent\/([\w-]+)\/output(?:\?.*)?$/, desc: 'Fetch final output.log for an agent', handler: handleAgentOutput },
7272
7303
  { method: 'GET', path: /^\/api\/agent\/([\w-]+)$/, desc: 'Get detailed agent info', handler: handleAgentDetail },
7273
7304
  { method: 'GET', path: /^\/api\/dispatch\/([\w.-]+)\/completion-report$/, desc: 'Read structured completion report for a dispatch', handler: (req, res, match) => {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "runtime": "copilot",
3
3
  "models": null,
4
- "cachedAt": "2026-05-07T17:21:57.007Z"
4
+ "cachedAt": "2026-05-07T17:23:11.001Z"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1772",
3
+ "version": "0.1.1773",
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"