@yemi33/minions 0.1.2431 → 0.1.2432

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.
@@ -33,6 +33,13 @@ function renderProjects(projects) {
33
33
  // a warning chip when branchMismatch (configured ≠ origin/HEAD) OR when the
34
34
  // local branch is behind main, and a compact "↑N ↓M" chip when ahead/behind
35
35
  // counts are non-zero.
36
+ //
37
+ // SEMANTICS SOURCE OF TRUTH: the branch/ahead-behind/dirty/main-drift semantics
38
+ // below are mirrored (data-only, no HTML) in dashboard/shared/project-git-summary.js
39
+ // so the Slim UX project-pill hover can render the SAME facts without forking
40
+ // this HTML renderer (CLAUDE.md "no duplicate solutions"). Keep the two in sync;
41
+ // this function stays the canonical HTML renderer (its unit tests extract it
42
+ // standalone, so it deliberately does not call the shared helper).
36
43
  function _renderProjectBranch(p) {
37
44
  if (!p) return '';
38
45
  if (p.gitState === 'missing') return '<span class="project-warn" title="Project localPath does not exist on disk">(path not found)</span>';
@@ -0,0 +1,239 @@
1
+ // dashboard/shared/project-git-summary.js
2
+ //
3
+ // Pure, DOM-free summarizer of a /api/status project object's git + dispatch
4
+ // state. This is the SINGLE source of truth for the *semantics* that the
5
+ // classic Projects bar renders as HTML chips in dashboard/js/render-other.js
6
+ // (_renderProjectBranch / _renderWorktreeModePill / _liveValidationTypesArray).
7
+ // Slim UX loads this helper (it does NOT load render-other.js) to build the
8
+ // project-pill hover panel without forking the classic branching logic
9
+ // (CLAUDE.md "no duplicate solutions — reuse before re-implementing").
10
+ //
11
+ // It intentionally returns plain data only — no HTML, no escaping, no DOM — so
12
+ // each surface renders it its own way (classic keeps its HTML chips; slim renders
13
+ // a hover popover + a title-attribute fallback). Keep the field semantics in
14
+ // lock-step with render-other.js; that file remains the canonical HTML renderer
15
+ // and its unit tests (ahead-behind-badge-comparator-label.test.js,
16
+ // dashboard-worktree-mode-pill.test.js) extract those functions standalone, so
17
+ // they are deliberately NOT refactored to call this helper.
18
+
19
+ // Mirrors render-other.js#_liveValidationTypesArray: normalize the single-string
20
+ // legacy shape OR an array of strings into an array of non-empty strings.
21
+ function _projectLiveValidationTypes(p) {
22
+ var raw = p && p.liveValidationType;
23
+ if (!raw) return [];
24
+ var arr = Array.isArray(raw) ? raw : [raw];
25
+ return arr.filter(function (t) { return typeof t === 'string' && t.length > 0; });
26
+ }
27
+
28
+ // Mirrors the checkout-mode classification in render-other.js#_renderWorktreeModePill.
29
+ // Returns { kind, label, types, autoValidate, detail } where kind is one of
30
+ // 'worktree' | 'live' | 'hybrid' | 'hybrid-invalid'.
31
+ function _projectCheckoutMode(p) {
32
+ if (p && p.checkoutMode === 'live') {
33
+ var types = _projectLiveValidationTypes(p);
34
+ if (p.liveValidationInvalid) {
35
+ var effective = types.length > 0 ? types.join(', ') : 'none';
36
+ return {
37
+ kind: 'hybrid-invalid',
38
+ label: 'Hybrid · invalid',
39
+ types: types,
40
+ autoValidate: false,
41
+ detail: 'Invalid hybrid configuration — unsupported settings are ignored. '
42
+ + 'Effective live work-item types: ' + effective + '.',
43
+ };
44
+ }
45
+ if (types.length > 0) {
46
+ var typesText = types.join(', ');
47
+ var autoDispatch = p.liveValidationAutoDispatch === true;
48
+ return {
49
+ kind: 'hybrid',
50
+ label: 'Hybrid · ' + typesText + (autoDispatch ? ' · auto-validate' : ''),
51
+ types: types,
52
+ autoValidate: autoDispatch,
53
+ detail: 'Hybrid dispatch — only the "' + typesText + '" work-item type(s) run '
54
+ + 'in-place; all others use isolated worktrees. '
55
+ + (autoDispatch
56
+ ? 'A validation work item is dispatched automatically after each eligible coding work item.'
57
+ : 'Auto-validation is OFF — validation must be dispatched manually.'),
58
+ };
59
+ }
60
+ return {
61
+ kind: 'live',
62
+ label: 'Live checkout',
63
+ types: [],
64
+ autoValidate: false,
65
+ detail: 'Live-checkout dispatch — agents run in-place inside the project working '
66
+ + 'tree (no isolated worktree); capped to one mutating dispatch.',
67
+ };
68
+ }
69
+ return {
70
+ kind: 'worktree',
71
+ label: 'Worktrees',
72
+ types: [],
73
+ autoValidate: false,
74
+ detail: 'Worktree dispatch mode (default) — each agent runs in its own git worktree.',
75
+ };
76
+ }
77
+
78
+ // Summarize a single project object from GET /api/status.
79
+ // Returns a plain object:
80
+ // {
81
+ // state, // 'missing'|'non-git'|'probe-timeout'|'stale'|'unknown'|'ok'
82
+ // available, // true only when git branch data is present + fresh
83
+ // refreshing, // true when the cached branch data is known-stale
84
+ // message, // short label for non-ok/unavailable states (or null)
85
+ // branch, mainBranch, remoteDefaultBranch,
86
+ // detached, dirty,
87
+ // branchDiffersFromMain,
88
+ // ahead, behind, comparator, branchDiffersFromComparator, showAheadBehind,
89
+ // warn, // null | { kind:'main-drift'|'behind', text, detail }
90
+ // mode, // { kind, label, types, autoValidate, detail }
91
+ // lines, // [{ label, value }] ready-to-render rows
92
+ // warnings, // [string] visible warning chips
93
+ // titleText, // multi-fact "•"-joined string for a title= fallback
94
+ // }
95
+ function summarizeProjectGit(p) {
96
+ var mode = _projectCheckoutMode(p || {});
97
+ var base = {
98
+ state: 'unknown',
99
+ available: false,
100
+ refreshing: false,
101
+ message: null,
102
+ branch: null,
103
+ mainBranch: null,
104
+ remoteDefaultBranch: null,
105
+ detached: false,
106
+ dirty: false,
107
+ branchDiffersFromMain: false,
108
+ ahead: null,
109
+ behind: null,
110
+ comparator: null,
111
+ branchDiffersFromComparator: false,
112
+ showAheadBehind: false,
113
+ warn: null,
114
+ mode: mode,
115
+ lines: [],
116
+ warnings: [],
117
+ titleText: '',
118
+ };
119
+
120
+ if (!p) return base;
121
+
122
+ // Non-ok git states — mirror the early returns of _renderProjectBranch.
123
+ if (p.gitState === 'missing') {
124
+ base.state = 'missing';
125
+ base.message = '(path not found)';
126
+ base.titleText = 'Project localPath does not exist on disk';
127
+ return base;
128
+ }
129
+ if (p.gitState === 'non-git') {
130
+ base.state = 'non-git';
131
+ base.message = '(not a git repo)';
132
+ base.titleText = 'Project path exists but is not a git repository';
133
+ return base;
134
+ }
135
+ if (p.gitState === 'probe-timeout') {
136
+ base.state = 'probe-timeout';
137
+ base.message = '(checking git…)';
138
+ base.titleText = 'Git status probe timed out (slow filesystem). The repository is valid and will refresh.';
139
+ return base;
140
+ }
141
+ if (p.gitState !== 'ok' || !p.gitBranch) {
142
+ // Nothing to show (classic returns an empty string here).
143
+ base.state = 'unknown';
144
+ return base;
145
+ }
146
+ if (p.gitStale) {
147
+ // Cached branch/dirty is known-stale — show a neutral refreshing state
148
+ // rather than a possibly-wrong branch name (parity with classic).
149
+ base.state = 'stale';
150
+ base.refreshing = true;
151
+ base.message = '(refreshing…)';
152
+ base.titleText = 'Branch changed outside the dashboard — refreshing…';
153
+ return base;
154
+ }
155
+
156
+ var branch = p.gitBranch;
157
+ var mainBranch = p.mainBranch || '';
158
+ var remoteDefault = p.remoteDefaultBranch || '';
159
+ var ahead = Number.isFinite(p.ahead) ? p.ahead : null;
160
+ var behind = Number.isFinite(p.behind) ? p.behind : null;
161
+ var branchDiffersFromMain = !!(mainBranch && branch !== mainBranch);
162
+ var comparator = mainBranch || remoteDefault;
163
+ // Comparator for ahead/behind is origin/<mainBranch || remoteDefaultBranch>
164
+ // (engine/queries.js _probeProjectGitStatus), never the branch's own upstream.
165
+ var comparatorRef = mainBranch || remoteDefault;
166
+ var branchDiffersFromComparator = !!(comparator && branch !== comparatorRef);
167
+ var showAheadBehind = ahead !== null && behind !== null && (ahead > 0 || behind > 0);
168
+
169
+ base.state = 'ok';
170
+ base.available = true;
171
+ base.branch = branch;
172
+ base.mainBranch = mainBranch || null;
173
+ base.remoteDefaultBranch = remoteDefault || null;
174
+ base.detached = !!p.gitDetached;
175
+ base.dirty = !!p.gitDirty;
176
+ base.branchDiffersFromMain = branchDiffersFromMain;
177
+ base.ahead = ahead;
178
+ base.behind = behind;
179
+ base.comparator = comparator || null;
180
+ base.branchDiffersFromComparator = branchDiffersFromComparator;
181
+ base.showAheadBehind = showAheadBehind;
182
+
183
+ // Warning chip — mirror _renderProjectBranch precedence: main drift wins over
184
+ // "behind".
185
+ if (p.branchMismatch) {
186
+ base.warn = {
187
+ kind: 'main-drift',
188
+ text: '⚠ main drift',
189
+ detail: 'Configured mainBranch (' + mainBranch + ') differs from origin/HEAD ('
190
+ + remoteDefault + ') — config is likely stale',
191
+ };
192
+ } else if (behind !== null && behind > 0) {
193
+ base.warn = {
194
+ kind: 'behind',
195
+ text: '⚠ behind',
196
+ detail: 'Local branch is ' + behind + ' commits behind origin/' + comparatorRef,
197
+ };
198
+ }
199
+
200
+ // Ready-to-render rows for a hover panel.
201
+ var branchValue = 'on: ' + branch
202
+ + (branchDiffersFromMain ? ' → ' + mainBranch : '')
203
+ + (base.detached ? ' (detached)' : '');
204
+ base.lines.push({ label: 'Branch', value: branchValue });
205
+ if (showAheadBehind) {
206
+ base.lines.push({
207
+ label: 'Ahead / behind',
208
+ value: '↑' + ahead + ' ↓' + behind
209
+ + (branchDiffersFromComparator ? ' vs ' + comparator : ''),
210
+ });
211
+ }
212
+ if (base.dirty) {
213
+ base.lines.push({ label: 'Working tree', value: '● uncommitted changes' });
214
+ }
215
+ base.lines.push({ label: 'Checkout', value: mode.label });
216
+ if (base.warn) base.warnings.push(base.warn.detail);
217
+
218
+ // "•"-joined multi-fact string for a title= attribute fallback. Mirrors the
219
+ // titleBits composition in _renderProjectBranch, then appends checkout mode.
220
+ var titleBits = ['on: ' + branch];
221
+ if (mainBranch) titleBits.push('configured main: ' + mainBranch);
222
+ if (remoteDefault) titleBits.push('origin/HEAD: ' + remoteDefault);
223
+ if (ahead !== null && behind !== null) titleBits.push('↑' + ahead + ' ↓' + behind + ' vs origin/' + comparatorRef);
224
+ if (base.detached) titleBits.push('(detached)');
225
+ if (base.dirty) titleBits.push('(dirty)');
226
+ titleBits.push('checkout: ' + mode.label);
227
+ if (base.warn) titleBits.push(base.warn.detail);
228
+ base.titleText = titleBits.join(' • ');
229
+
230
+ return base;
231
+ }
232
+
233
+ var MinionsProjectGitSummary = {
234
+ summarize: summarizeProjectGit,
235
+ liveValidationTypes: _projectLiveValidationTypes,
236
+ checkoutMode: _projectCheckoutMode,
237
+ };
238
+
239
+ if (typeof window !== 'undefined') window.MinionsProjectGitSummary = MinionsProjectGitSummary;
@@ -25,6 +25,113 @@
25
25
  empty.textContent = 'No projects linked yet.';
26
26
  return empty;
27
27
  }
28
+ // ── Project-pill hover detail popover ────────────────────────
29
+ // Parity with the CLASSIC Projects bar (dashboard/js/render-other.js
30
+ // _renderProjectBranch / _renderWorktreeModePill): on hover/focus a pill shows
31
+ // the per-project git + dispatch detail — current branch, "→ main" drift,
32
+ // ahead/behind vs origin/main, dirty working tree, main-drift/behind warnings,
33
+ // and the checkout mode (Worktrees / Live checkout / Hybrid + validation types).
34
+ // All the semantics come from the SHARED, DOM-free summarizer
35
+ // window.MinionsProjectGitSummary.summarize (dashboard/shared/project-git-summary.js)
36
+ // so we don't fork the classic branching logic. Content is built with
37
+ // textContent/createElement only (no innerHTML — SEC-03 ratchet).
38
+ var _pillPopoverEl = null;
39
+ var _pillPopoverEscBound = false;
40
+
41
+ function _ensurePillPopover() {
42
+ if (_pillPopoverEl && document.body.contains(_pillPopoverEl)) return _pillPopoverEl;
43
+ var el = document.createElement('div');
44
+ el.className = 'slim-pill-popover';
45
+ el.id = 'slim-project-pill-popover';
46
+ el.setAttribute('role', 'tooltip');
47
+ el.hidden = true;
48
+ document.body.appendChild(el);
49
+ _pillPopoverEl = el;
50
+ if (!_pillPopoverEscBound) {
51
+ // Escape dismisses the popover (accessible non-blocking tooltip).
52
+ document.addEventListener('keydown', function(ev) {
53
+ if (ev.key === 'Escape' && _pillPopoverEl && !_pillPopoverEl.hidden) hidePillPopover();
54
+ });
55
+ _pillPopoverEscBound = true;
56
+ }
57
+ return el;
58
+ }
59
+
60
+ function _appendPopoverRow(parent, label, value, cls) {
61
+ var row = document.createElement('div');
62
+ row.className = 'slim-pill-popover-row' + (cls ? ' ' + cls : '');
63
+ if (label) {
64
+ var l = document.createElement('span');
65
+ l.className = 'slim-pill-popover-label';
66
+ l.textContent = label;
67
+ row.appendChild(l);
68
+ }
69
+ var v = document.createElement('span');
70
+ v.className = 'slim-pill-popover-value';
71
+ v.textContent = value;
72
+ row.appendChild(v);
73
+ parent.appendChild(row);
74
+ }
75
+
76
+ // Populate + position the popover under `pill` for the given status project
77
+ // object. No-op (and hides any open popover) when there is nothing to show.
78
+ function showPillPopover(pill, project) {
79
+ if (!pill || !project) return;
80
+ var summ = (window.MinionsProjectGitSummary && window.MinionsProjectGitSummary.summarize)
81
+ ? window.MinionsProjectGitSummary.summarize(project)
82
+ : null;
83
+ if (!summ) return;
84
+ var el = _ensurePillPopover();
85
+ el.textContent = '';
86
+
87
+ var header = document.createElement('div');
88
+ header.className = 'slim-pill-popover-header';
89
+ header.textContent = String(project.displayName || project.name || '');
90
+ el.appendChild(header);
91
+
92
+ if (summ.available) {
93
+ summ.lines.forEach(function(line) {
94
+ _appendPopoverRow(el, line.label, line.value);
95
+ });
96
+ if (summ.warn) {
97
+ _appendPopoverRow(el, '', summ.warn.text + ' — ' + summ.warn.detail, 'slim-pill-popover-warn');
98
+ }
99
+ } else if (summ.refreshing) {
100
+ _appendPopoverRow(el, '', summ.message || 'Refreshing…', 'slim-pill-popover-muted');
101
+ } else if (summ.message) {
102
+ _appendPopoverRow(el, '', summ.message, 'slim-pill-popover-muted');
103
+ _appendPopoverRow(el, 'Checkout', summ.mode.label);
104
+ } else {
105
+ // No git branch data (e.g. non-project path) — still show checkout mode.
106
+ _appendPopoverRow(el, 'Checkout', summ.mode.label);
107
+ }
108
+
109
+ // Link the pill to the popover for assistive tech while it's visible.
110
+ pill.setAttribute('aria-describedby', el.id);
111
+ el.hidden = false;
112
+
113
+ // Position below the pill, clamped to the viewport.
114
+ var r = pill.getBoundingClientRect();
115
+ var top = r.bottom + 6;
116
+ var left = r.left;
117
+ var maxLeft = window.innerWidth - el.offsetWidth - 8;
118
+ if (left > maxLeft) left = Math.max(8, maxLeft);
119
+ if (left < 8) left = 8;
120
+ el.style.top = top + 'px';
121
+ el.style.left = left + 'px';
122
+ }
123
+
124
+ function hidePillPopover() {
125
+ if (!_pillPopoverEl) return;
126
+ _pillPopoverEl.hidden = true;
127
+ // Drop stale aria-describedby links on all pills.
128
+ var group = document.getElementById('chat-context-pills');
129
+ if (group) {
130
+ var pills = group.querySelectorAll('.context-pill[aria-describedby]');
131
+ for (var i = 0; i < pills.length; i++) pills[i].removeAttribute('aria-describedby');
132
+ }
133
+ }
134
+
28
135
  // Strip variant: project pills. One selectable pill per project, single-select.
29
136
  // No auto-default: when nothing is explicitly chosen, no pill is active and the
30
137
  // indicator stays in the "Select project" state, so the picker never silently
@@ -32,7 +139,11 @@
32
139
  // choice back off. Returns a container of pill buttons built as real DOM nodes
33
140
  // (no innerHTML — SEC-03 ratchet, see test/unit.test.js DYNAMIC_INNERHTML_BASELINE);
34
141
  // the caller attaches a click listener via event delegation.
35
- function makeContextPills(projects, selected, displayByName) {
142
+ //
143
+ // `projectByName` maps project name → the full /api/status project object so
144
+ // each pill can surface its git + checkout detail on hover/focus (see
145
+ // showPillPopover). It is optional — pills still render name-only without it.
146
+ function makeContextPills(projects, selected, displayByName, projectByName) {
36
147
  var group = document.createElement('span');
37
148
  group.id = 'chat-context-pills';
38
149
  group.className = 'context-pills';
@@ -49,15 +160,37 @@
49
160
  var isActive = p === selected;
50
161
  pill.classList.toggle('active', isActive);
51
162
  pill.setAttribute('aria-checked', isActive ? 'true' : 'false');
163
+ var proj = projectByName && projectByName[p];
164
+ if (proj) {
165
+ // title= is the graceful fallback (works with no JS / reduced-motion);
166
+ // the richer popover is layered on via hover/focus below.
167
+ var summ = (window.MinionsProjectGitSummary && window.MinionsProjectGitSummary.summarize)
168
+ ? window.MinionsProjectGitSummary.summarize(proj)
169
+ : null;
170
+ if (summ && summ.titleText) pill.title = summ.titleText;
171
+ _wirePillHover(pill, proj);
172
+ }
52
173
  group.appendChild(pill);
53
174
  }
54
175
  return group;
55
176
  }
177
+
178
+ // Bind hover + keyboard-focus reveal of the detail popover for one pill.
179
+ // Kept in a helper so the closure captures the correct project object per
180
+ // iteration (the loop uses `var`).
181
+ function _wirePillHover(pill, proj) {
182
+ pill.addEventListener('mouseenter', function() { showPillPopover(pill, proj); });
183
+ pill.addEventListener('mouseleave', hidePillPopover);
184
+ pill.addEventListener('focus', function() { showPillPopover(pill, proj); });
185
+ pill.addEventListener('blur', hidePillPopover);
186
+ }
56
187
  async function loadProjectsAndRenderContext(opts) {
57
188
  var stripEl = document.getElementById('chat-context-strip');
58
189
  var controlsEl = document.getElementById('chat-context-controls');
59
190
  if (!stripEl || !controlsEl) return;
60
191
  var preferProject = (opts && opts.preferProject) || null;
192
+ // Any prior hover popover belongs to pills we're about to replace.
193
+ if (typeof hidePillPopover === 'function') hidePillPopover();
61
194
  try {
62
195
  var res = await fetch('/api/status');
63
196
  if (!res.ok) throw new Error('HTTP ' + res.status);
@@ -66,7 +199,12 @@
66
199
  .filter(function(p) { return p && p.name; });
67
200
  var projects = projectList.map(function(p) { return String(p.name); });
68
201
  var displayByName = {};
69
- projectList.forEach(function(p) { displayByName[String(p.name)] = String(p.displayName || p.name); });
202
+ var projectByName = {};
203
+ projectList.forEach(function(p) {
204
+ var key = String(p.name);
205
+ displayByName[key] = String(p.displayName || p.name);
206
+ projectByName[key] = p;
207
+ });
70
208
  if (projects.length === 0) {
71
209
  stripEl.style.display = '';
72
210
  controlsEl.replaceChildren(makeContextEmptyNode(), makeContextAddBtn());
@@ -84,7 +222,7 @@
84
222
  // deliberately do NOT fall back to projects[0] here (W-mqayzsj3) so CC
85
223
  // turns and project-scoped actions don't silently target the wrong
86
224
  // project.
87
- var pills = makeContextPills(projects, currentProject, displayByName);
225
+ var pills = makeContextPills(projects, currentProject, displayByName, projectByName);
88
226
  stripEl.style.display = '';
89
227
  controlsEl.replaceChildren(pills, makeContextAddBtn());
90
228
  pills.addEventListener('click', function(ev) {
@@ -576,6 +576,48 @@
576
576
  .chat-context-strip .context-pill.active:hover {
577
577
  background: var(--blue);
578
578
  }
579
+ /* Project-pill hover detail popover (parity with the classic Projects bar).
580
+ Fixed-position, pointer-events:none so it never traps the pointer or
581
+ focus — it's a passive tooltip dismissed on mouseleave/blur/Escape. */
582
+ .slim-pill-popover {
583
+ position: fixed;
584
+ z-index: 1200;
585
+ max-width: 320px;
586
+ pointer-events: none;
587
+ background: var(--surface2);
588
+ border: 1px solid var(--border);
589
+ border-radius: var(--radius, 8px);
590
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);
591
+ padding: var(--space-2) var(--space-3);
592
+ font-size: var(--text-sm, 12px);
593
+ line-height: 1.4;
594
+ color: var(--text);
595
+ }
596
+ .slim-pill-popover[hidden] { display: none; }
597
+ .slim-pill-popover-header {
598
+ font-weight: 700;
599
+ color: var(--blue);
600
+ margin-bottom: 4px;
601
+ white-space: nowrap;
602
+ overflow: hidden;
603
+ text-overflow: ellipsis;
604
+ }
605
+ .slim-pill-popover-row {
606
+ display: flex;
607
+ gap: var(--space-2);
608
+ align-items: baseline;
609
+ }
610
+ .slim-pill-popover-label {
611
+ color: var(--muted);
612
+ flex: 0 0 auto;
613
+ min-width: 84px;
614
+ }
615
+ .slim-pill-popover-value {
616
+ color: var(--text);
617
+ word-break: break-word;
618
+ }
619
+ .slim-pill-popover-muted .slim-pill-popover-value { color: var(--muted); font-style: italic; }
620
+ .slim-pill-popover-warn .slim-pill-popover-value { color: var(--red); }
579
621
  .chat-context-strip .context-static {
580
622
  color: var(--text);
581
623
  font-weight: 500;
@@ -8,7 +8,7 @@ const shared = require('./engine/shared');
8
8
  const { safeRead } = shared;
9
9
 
10
10
  const MINIONS_DIR = __dirname;
11
- const DASHBOARD_SHARED_JS = ['pr-merge-state', 'pr-filters', 'cc-suggestions', 'model-display', 'watches-source', 'welcome-popup'];
11
+ const DASHBOARD_SHARED_JS = ['pr-merge-state', 'pr-filters', 'cc-suggestions', 'model-display', 'watches-source', 'project-git-summary', 'welcome-popup'];
12
12
 
13
13
  function buildDashboardHtml() {
14
14
  const dashDir = path.join(MINIONS_DIR, 'dashboard');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2431",
3
+ "version": "0.1.2432",
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"