@yemi33/minions 0.1.2255 → 0.1.2257
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/dashboard/js/charter-editor.js +85 -0
- package/dashboard/js/detail-panel.js +7 -56
- package/dashboard/js/refresh.js +2 -1
- package/dashboard/js/render-work-items.js +2 -3
- package/dashboard/js/settings.js +40 -5
- package/dashboard/slim/body.html +5 -0
- package/dashboard/slim/js/members.js +112 -0
- package/dashboard/slim/js/projects.js +48 -35
- package/dashboard/slim/styles.css +72 -14
- package/dashboard-build.js +1 -1
- package/dashboard.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// dashboard/js/charter-editor.js — shared agent charter editor widget.
|
|
2
|
+
//
|
|
3
|
+
// Backs BOTH the agent detail panel (single instance, scope 'detail') and the
|
|
4
|
+
// Settings Agents table (one instance per agent, scope 'settings-<id>'). All IDs
|
|
5
|
+
// are suffixed with a caller-supplied `scope` so multiple editors coexist on the
|
|
6
|
+
// same page. Raw content is kept in a module-level map (outside the DOM) so a
|
|
7
|
+
// surrounding innerHTML rewrite — e.g. switching detail-panel tabs — never loses
|
|
8
|
+
// the operator's unsaved text. Saves go through the existing
|
|
9
|
+
// POST /api/agents/charter endpoint; do NOT add a second endpoint.
|
|
10
|
+
|
|
11
|
+
const _charterStateByScope = Object.create(null); // scope -> { agentId, raw }
|
|
12
|
+
|
|
13
|
+
// Returns the editor markup as a string. Side effect: records per-scope state
|
|
14
|
+
// (agentId + raw content) so the toggle/cancel/save handlers can find it without
|
|
15
|
+
// threading data through the DOM. `content` is used verbatim (callers pass
|
|
16
|
+
// `detail.charter || ''`), preserving the detail-panel's pre-existing behavior.
|
|
17
|
+
function renderCharterEditor(opts) {
|
|
18
|
+
const agentId = (opts && opts.agentId) || '';
|
|
19
|
+
const content = (opts && opts.content) || '';
|
|
20
|
+
const scope = (opts && opts.scope) || 'detail';
|
|
21
|
+
_charterStateByScope[scope] = { agentId, raw: content };
|
|
22
|
+
const s = escHtml(scope);
|
|
23
|
+
const a = escHtml(agentId);
|
|
24
|
+
return '' +
|
|
25
|
+
'<div style="display:flex;gap:6px;margin-bottom:8px">' +
|
|
26
|
+
'<button class="pr-pager-btn" id="charter-edit-btn-' + s + '" style="font-size:var(--text-sm);padding:2px 10px" onclick="_toggleCharterEdit(\'' + s + '\')">Edit</button>' +
|
|
27
|
+
'<button class="modal-copy is-success" id="charter-save-btn-' + s + '" style="display:none" onclick="_saveCharter(\'' + s + '\',\'' + a + '\')">Save</button>' +
|
|
28
|
+
'<button class="pr-pager-btn" id="charter-cancel-btn-' + s + '" style="font-size:var(--text-sm);padding:2px 10px;display:none" onclick="_cancelCharterEdit(\'' + s + '\')">Cancel</button>' +
|
|
29
|
+
'</div>' +
|
|
30
|
+
'<div id="charter-view-' + s + '" class="section">' + renderMd(content || 'No charter found. Click Edit to create one.') + '</div>' +
|
|
31
|
+
'<textarea id="charter-editor-' + s + '" style="display:none;width:100%;min-height:300px;padding:8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-family:Consolas,monospace;font-size:var(--text-md);resize:vertical">' + escHtml(content) + '</textarea>';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function _toggleCharterEdit(scope) {
|
|
35
|
+
scope = scope || 'detail';
|
|
36
|
+
document.getElementById('charter-view-' + scope).style.display = 'none';
|
|
37
|
+
document.getElementById('charter-editor-' + scope).style.display = '';
|
|
38
|
+
document.getElementById('charter-edit-btn-' + scope).style.display = 'none';
|
|
39
|
+
document.getElementById('charter-save-btn-' + scope).style.display = '';
|
|
40
|
+
document.getElementById('charter-cancel-btn-' + scope).style.display = '';
|
|
41
|
+
document.getElementById('charter-editor-' + scope).focus();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function _cancelCharterEdit(scope) {
|
|
45
|
+
scope = scope || 'detail';
|
|
46
|
+
const st = _charterStateByScope[scope] || {};
|
|
47
|
+
document.getElementById('charter-editor-' + scope).value = st.raw || '';
|
|
48
|
+
document.getElementById('charter-view-' + scope).style.display = '';
|
|
49
|
+
document.getElementById('charter-editor-' + scope).style.display = 'none';
|
|
50
|
+
document.getElementById('charter-edit-btn-' + scope).style.display = '';
|
|
51
|
+
document.getElementById('charter-save-btn-' + scope).style.display = 'none';
|
|
52
|
+
document.getElementById('charter-cancel-btn-' + scope).style.display = 'none';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function _saveCharter(scope, agentId) {
|
|
56
|
+
scope = scope || 'detail';
|
|
57
|
+
const st = _charterStateByScope[scope] || {};
|
|
58
|
+
const id = agentId || st.agentId;
|
|
59
|
+
if (!id) { alert('No agent selected'); return; }
|
|
60
|
+
const content = document.getElementById('charter-editor-' + scope).value;
|
|
61
|
+
const btn = document.getElementById('charter-save-btn-' + scope);
|
|
62
|
+
// Optimistic: flip the button to a saving state before the await; revert on error.
|
|
63
|
+
btn.textContent = 'Saving...'; btn.style.pointerEvents = 'none';
|
|
64
|
+
try {
|
|
65
|
+
const res = await fetch('/api/agents/charter', {
|
|
66
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
67
|
+
body: JSON.stringify({ agent: id, content })
|
|
68
|
+
});
|
|
69
|
+
if (res.ok) {
|
|
70
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd() escapes all user-controlled fields before assembling HTML (see dashboard/js/utils.js)
|
|
71
|
+
document.getElementById('charter-view-' + scope).innerHTML = renderMd(content);
|
|
72
|
+
_charterStateByScope[scope] = { agentId: id, raw: content };
|
|
73
|
+
_cancelCharterEdit(scope);
|
|
74
|
+
showToast('cmd-toast', 'Charter saved', true);
|
|
75
|
+
} else {
|
|
76
|
+
const d = await res.json().catch(() => ({}));
|
|
77
|
+
alert('Save failed: ' + (d.error || 'unknown'));
|
|
78
|
+
}
|
|
79
|
+
} catch (e) { alert('Save failed: ' + e.message); }
|
|
80
|
+
btn.textContent = 'Save'; btn.style.pointerEvents = '';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
window.MinionsCharterEditor = { renderCharterEditor, _toggleCharterEdit, _cancelCharterEdit, _saveCharter };
|
|
85
|
+
} catch (e) { /* non-browser (unit source-inspection) */ }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// dashboard/js/detail-panel.js — Agent detail panel extracted from dashboard.html
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// The Charter tab reuses the shared charter editor widget (dashboard/js/charter-editor.js),
|
|
3
|
+
// which also backs the Settings Agents table. Raw content survives innerHTML rewrites
|
|
4
|
+
// on tab switch via the widget's own scope-keyed state map.
|
|
4
5
|
|
|
5
6
|
function closeDetail() {
|
|
6
7
|
document.getElementById('detail-overlay').classList.remove('open');
|
|
@@ -201,17 +202,10 @@ function renderDetailContent(detail, tab) {
|
|
|
201
202
|
'</div>';
|
|
202
203
|
startLiveStream(currentAgentId);
|
|
203
204
|
} else if (tab === 'charter') {
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
'<button class="pr-pager-btn" id="charter-edit-btn" style="font-size:var(--text-sm);padding:2px 10px" onclick="_toggleCharterEdit()">Edit</button>' +
|
|
209
|
-
'<button class="modal-copy is-success" id="charter-save-btn" style="display:none" onclick="_saveCharter()">Save</button>' +
|
|
210
|
-
'<button class="pr-pager-btn" id="charter-cancel-btn" style="font-size:var(--text-sm);padding:2px 10px;display:none" onclick="_cancelCharterEdit()">Cancel</button>' +
|
|
211
|
-
'</div>' +
|
|
212
|
-
'<div id="charter-view" class="section">' + renderMd(charterContent || 'No charter found. Click Edit to create one.') + '</div>' +
|
|
213
|
-
'<textarea id="charter-editor" style="display:none;width:100%;min-height:300px;padding:8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-family:Consolas,monospace;font-size:var(--text-md);resize:vertical">' + escHtml(charterContent) + '</textarea>';
|
|
214
|
-
_charterRawCache = charterContent;
|
|
205
|
+
// Shared charter widget (scope 'detail') — same render/save logic the
|
|
206
|
+
// Settings Agents table uses. Pass charter verbatim to preserve behavior.
|
|
207
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderCharterEditor() escapes all user-controlled fields (renderMd/escHtml) before assembling HTML (see dashboard/js/charter-editor.js)
|
|
208
|
+
el.innerHTML = renderCharterEditor({ agentId: currentAgentId, content: detail.charter || '', scope: 'detail' });
|
|
215
209
|
} else if (tab === 'history') {
|
|
216
210
|
let html = '';
|
|
217
211
|
// Recent dispatch results
|
|
@@ -240,47 +234,4 @@ function renderDetailContent(detail, tab) {
|
|
|
240
234
|
}
|
|
241
235
|
}
|
|
242
236
|
|
|
243
|
-
function _toggleCharterEdit() {
|
|
244
|
-
document.getElementById('charter-view').style.display = 'none';
|
|
245
|
-
document.getElementById('charter-editor').style.display = '';
|
|
246
|
-
document.getElementById('charter-edit-btn').style.display = 'none';
|
|
247
|
-
document.getElementById('charter-save-btn').style.display = '';
|
|
248
|
-
document.getElementById('charter-cancel-btn').style.display = '';
|
|
249
|
-
document.getElementById('charter-editor').focus();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function _cancelCharterEdit() {
|
|
253
|
-
const el = document.getElementById('detail-content');
|
|
254
|
-
document.getElementById('charter-editor').value = _charterRawCache || '';
|
|
255
|
-
document.getElementById('charter-view').style.display = '';
|
|
256
|
-
document.getElementById('charter-editor').style.display = 'none';
|
|
257
|
-
document.getElementById('charter-edit-btn').style.display = '';
|
|
258
|
-
document.getElementById('charter-save-btn').style.display = 'none';
|
|
259
|
-
document.getElementById('charter-cancel-btn').style.display = 'none';
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
async function _saveCharter() {
|
|
263
|
-
if (!currentAgentId) { alert('No agent selected'); return; }
|
|
264
|
-
const content = document.getElementById('charter-editor').value;
|
|
265
|
-
const btn = document.getElementById('charter-save-btn');
|
|
266
|
-
btn.textContent = 'Saving...'; btn.style.pointerEvents = 'none';
|
|
267
|
-
try {
|
|
268
|
-
const res = await fetch('/api/agents/charter', {
|
|
269
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
270
|
-
body: JSON.stringify({ agent: currentAgentId, content })
|
|
271
|
-
});
|
|
272
|
-
if (res.ok) {
|
|
273
|
-
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd() escapes all user-controlled fields before assembling HTML (see dashboard/js/utils.js)
|
|
274
|
-
document.getElementById('charter-view').innerHTML = renderMd(content);
|
|
275
|
-
_charterRawCache = content;
|
|
276
|
-
_cancelCharterEdit();
|
|
277
|
-
showToast('cmd-toast', 'Charter saved', true);
|
|
278
|
-
} else {
|
|
279
|
-
const d = await res.json().catch(() => ({}));
|
|
280
|
-
alert('Save failed: ' + (d.error || 'unknown'));
|
|
281
|
-
}
|
|
282
|
-
} catch (e) { alert('Save failed: ' + e.message); }
|
|
283
|
-
btn.textContent = 'Save'; btn.style.pointerEvents = '';
|
|
284
|
-
}
|
|
285
|
-
|
|
286
237
|
window.MinionsDetail = { closeDetail, renderDetailTabs, switchTab, renderDetailContent };
|
package/dashboard/js/refresh.js
CHANGED
|
@@ -165,7 +165,8 @@ const RENDER_VERSIONS = {
|
|
|
165
165
|
// renderers) can call `_changed('settings', …)` and bump on visual revamps.
|
|
166
166
|
// Bumped to 2 by W-mpmwxkcn000646cc (left-rail tabbed Settings layout).
|
|
167
167
|
// Bumped to 3 by W-mqk2s8q1 (Advanced → Diagnostics: relocated the diag button).
|
|
168
|
-
|
|
168
|
+
// Bumped to 4 by W-mqrdavob (Agents table: per-agent charter editor column).
|
|
169
|
+
settings: 4,
|
|
169
170
|
};
|
|
170
171
|
const _sectionCache = {};
|
|
171
172
|
const _lastValueByKey = {};
|
|
@@ -190,7 +190,6 @@ function wiRow(item) {
|
|
|
190
190
|
followupChip = ' <a class="pr-badge draft" style="font-size:var(--text-xs);text-decoration:none" target="_blank" rel="noopener" href="' + escapeHtml(prFollowup.parent_pr_url) + '" title="Follow-up dispatched from ' + escapeHtml(prRef) + (prFollowup.parent_comment_author ? ' by ' + escapeHtml(prFollowup.parent_comment_author) : '') + '" onclick="event.stopPropagation()">↩ from ' + escapeHtml(prLabel) + '</a>';
|
|
191
191
|
}
|
|
192
192
|
return '<tr data-wi-id="' + escapeHtml(item.id) + '" style="cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openWorkItemDetail(\'' + escapeHtml(item.id) + '\')">' +
|
|
193
|
-
'<td><span class="pr-id">' + escapeHtml(item.id || '') + '</span></td>' +
|
|
194
193
|
'<td style="min-width:280px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escapeHtml((item.title || '').slice(0, 200)) + '">' + escapeHtml(item.title || '') + followupChip + '</td>' +
|
|
195
194
|
'<td><span style="font-size:var(--text-sm);color:var(--muted)">' + escapeHtml(item._source || '') + '</span>' +
|
|
196
195
|
(item.scope === 'fan-out' ? ' <span class="pr-badge ' + (item.status === 'done' || item.status === 'failed' ? 'draft' : 'building') + '" style="font-size:var(--text-xs)">fan-out</span>' : '') + '</td>' +
|
|
@@ -286,7 +285,7 @@ function renderWorkItems(items, opts) {
|
|
|
286
285
|
const start = wiPage * WI_PER_PAGE;
|
|
287
286
|
const pageItems = items.slice(start, start + WI_PER_PAGE);
|
|
288
287
|
|
|
289
|
-
let html = '<div class="pr-table-wrap"><table class="pr-table"><thead><tr><th>
|
|
288
|
+
let html = '<div class="pr-table-wrap"><table class="pr-table"><thead><tr><th>Title</th><th>Project</th><th>Type</th><th>Priority</th><th>Status</th><th>Agent</th><th>PR</th><th>Created</th><th></th><th></th></tr></thead><tbody>';
|
|
290
289
|
html += pageItems.map(wiRow).join('');
|
|
291
290
|
html += '</tbody></table></div>';
|
|
292
291
|
|
|
@@ -1061,7 +1060,7 @@ function openWorkItemDetail(id) {
|
|
|
1061
1060
|
|
|
1062
1061
|
function openAllWorkItems() {
|
|
1063
1062
|
document.getElementById('modal-title').textContent = 'All Work Items (' + allWorkItems.length + ')';
|
|
1064
|
-
const html = '<div class="pr-table-wrap"><table class="pr-table"><thead><tr><th>
|
|
1063
|
+
const html = '<div class="pr-table-wrap"><table class="pr-table"><thead><tr><th>Title</th><th>Project</th><th>Type</th><th>Priority</th><th>Status</th><th>Agent</th><th>PR</th><th>Created</th><th></th><th></th></tr></thead><tbody>' +
|
|
1065
1064
|
allWorkItems.map(wiRow).join('') + '</tbody></table></div>';
|
|
1066
1065
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escapeHtml() by wiRow() (fields: work item id/title/source/status/agent/PR links/dates/follow-up metadata)
|
|
1067
1066
|
document.getElementById('modal-body').innerHTML = html;
|
package/dashboard/js/settings.js
CHANGED
|
@@ -82,7 +82,15 @@ async function openSettings() {
|
|
|
82
82
|
'<input value="' + escHtml(a.model || '') + '" placeholder="' + escHtml(fleetModelLabel) + ' (fleet)" disabled style="width:120px;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--muted);font-size:var(--text-base)">' +
|
|
83
83
|
'</td>' +
|
|
84
84
|
'<td><input data-agent="' + escHtml(id) + '" data-field="monthlyBudgetUsd" value="' + escHtml(a.monthlyBudgetUsd != null ? String(a.monthlyBudgetUsd) : '') + '" placeholder="unlimited" style="width:70px;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:var(--text-base);text-align:right"></td>' +
|
|
85
|
-
|
|
85
|
+
// Charter editor opens the SAME shared widget as the agent detail panel
|
|
86
|
+
// (dashboard/js/charter-editor.js); content is lazy-loaded on first open.
|
|
87
|
+
'<td style="text-align:center"><button type="button" class="pr-pager-btn" id="charter-toggle-' + escHtml(id) + '" style="font-size:var(--text-sm);padding:2px 10px" onclick="_toggleSettingsCharter(\'' + escHtml(id) + '\')">Show Charter</button></td>' +
|
|
88
|
+
'</tr>' +
|
|
89
|
+
// Hidden expander row — the shared charter widget is rendered into the cell
|
|
90
|
+
// the first time the operator opens it (see _toggleSettingsCharter).
|
|
91
|
+
'<tr id="charter-row-' + escHtml(id) + '" style="display:none"><td colspan="7" style="padding:8px 4px 16px">' +
|
|
92
|
+
'<div id="charter-cell-' + escHtml(id) + '" style="color:var(--muted)">Loading charter…</div>' +
|
|
93
|
+
'</td></tr>';
|
|
86
94
|
}).join('');
|
|
87
95
|
|
|
88
96
|
// ── Section bodies — every original control is grouped under one of these
|
|
@@ -91,7 +99,7 @@ async function openSettings() {
|
|
|
91
99
|
// wrapping the 8 advanced runtime toggles was split into Copilot Tuning and
|
|
92
100
|
// Claude Tuning tabs so each runtime's knobs live behind its own rail entry.
|
|
93
101
|
const paneRuntime =
|
|
94
|
-
'<h3>Runtime & Models</h3>' +
|
|
102
|
+
'<h3>Agents Runtime & Models</h3>' +
|
|
95
103
|
'<div class="settings-pane-sub">Single source of truth for which CLI runtime + model the fleet spawns. Per-agent overrides live in the Agents table below.</div>' +
|
|
96
104
|
'<div id="set-runtime-section" style="border:1px solid var(--border);border-radius:6px;padding:10px 12px;margin-bottom:16px">' +
|
|
97
105
|
'<div style="display:grid;grid-template-columns:1fr 2fr;gap:8px;margin-bottom:8px">' +
|
|
@@ -152,7 +160,7 @@ async function openSettings() {
|
|
|
152
160
|
'<h4>Agents</h4>' +
|
|
153
161
|
'<div style="font-size:var(--text-sm);color:var(--muted);margin-bottom:6px">CLI / Model placeholders show the fleet default each agent will inherit. Pick a value to pin per-agent; clear to re-inherit. Per-agent monthly budget overrides the fleet ceiling.</div>' +
|
|
154
162
|
'<table style="width:100%;border-collapse:collapse;margin-bottom:16px;font-size:var(--text-base)">' +
|
|
155
|
-
'<tr style="text-align:left;color:var(--muted)"><th style="padding:4px">Agent</th><th style="padding:4px">Role</th><th style="padding:4px">Skills</th><th style="padding:4px">CLI</th><th style="padding:4px">Model</th><th style="padding:4px">Budget $/mo</th></tr>' +
|
|
163
|
+
'<tr style="text-align:left;color:var(--muted)"><th style="padding:4px">Agent</th><th style="padding:4px">Role</th><th style="padding:4px">Skills</th><th style="padding:4px">CLI</th><th style="padding:4px">Model</th><th style="padding:4px">Budget $/mo</th><th style="padding:4px">Charter</th></tr>' +
|
|
156
164
|
agentRows +
|
|
157
165
|
'</table>';
|
|
158
166
|
|
|
@@ -447,7 +455,7 @@ async function openSettings() {
|
|
|
447
455
|
|
|
448
456
|
const paneBudget =
|
|
449
457
|
'<h3>Budget</h3>' +
|
|
450
|
-
'<div class="settings-pane-sub">Fleet-wide spend ceiling. Per-agent monthly caps live in the Agents table on the Runtime & Models tab.</div>' +
|
|
458
|
+
'<div class="settings-pane-sub">Fleet-wide spend ceiling. Per-agent monthly caps live in the Agents table on the Agents Runtime & Models tab.</div>' +
|
|
451
459
|
'<div class="settings-grid-2">' +
|
|
452
460
|
settingsField('Max budget (USD)', 'set-maxBudgetUsd', e.maxBudgetUsd != null ? String(e.maxBudgetUsd) : '', '', 'Fleet ceiling for --max-budget-usd. 0 is a valid cap (read-only / dry-run). Empty = no cap. Claude only.') +
|
|
453
461
|
'</div>';
|
|
@@ -543,7 +551,7 @@ async function openSettings() {
|
|
|
543
551
|
// adjacent to Auto-fix so operators following the legacy mental model find
|
|
544
552
|
// the moved flags quickly.
|
|
545
553
|
const sections = [
|
|
546
|
-
{ id: 'runtime', label: 'Runtime & Models', featured: true, html: paneRuntime },
|
|
554
|
+
{ id: 'runtime', label: 'Agents Runtime & Models', featured: true, html: paneRuntime },
|
|
547
555
|
{ id: 'autofix', label: 'Auto-fix & Review Loop', featured: true, html: paneAutoFix },
|
|
548
556
|
{ id: 'lifecycle', label: 'PR Lifecycle', html: paneLifecycle },
|
|
549
557
|
{ id: 'workflow', label: 'Workflow Defaults', html: paneWorkflow },
|
|
@@ -696,6 +704,33 @@ async function openSettings() {
|
|
|
696
704
|
});
|
|
697
705
|
}
|
|
698
706
|
|
|
707
|
+
// Lazily open/close the per-agent charter editor in the Settings Agents table.
|
|
708
|
+
// The charter content is NOT included in /api/settings, so the first open fetches
|
|
709
|
+
// it from /api/agent/<id> and renders the shared charter widget (scope
|
|
710
|
+
// 'settings-<id>') into the expander cell — the same widget the detail panel uses.
|
|
711
|
+
async function _toggleSettingsCharter(agentId) {
|
|
712
|
+
const row = document.getElementById('charter-row-' + agentId);
|
|
713
|
+
const cell = document.getElementById('charter-cell-' + agentId);
|
|
714
|
+
if (!row || !cell) return;
|
|
715
|
+
const isOpen = row.style.display !== 'none';
|
|
716
|
+
if (isOpen) { row.style.display = 'none'; return; }
|
|
717
|
+
row.style.display = '';
|
|
718
|
+
if (cell.dataset.loaded) return; // already rendered — just re-expand
|
|
719
|
+
cell.textContent = 'Loading charter…';
|
|
720
|
+
try {
|
|
721
|
+
const res = await fetch('/api/agent/' + encodeURIComponent(agentId));
|
|
722
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
723
|
+
const detail = await res.json();
|
|
724
|
+
const html = renderCharterEditor({ agentId, content: detail.charter || '', scope: 'settings-' + agentId });
|
|
725
|
+
cell.textContent = '';
|
|
726
|
+
// eslint-disable-next-line no-unsanitized/method -- reason: renderCharterEditor() escapes all user-controlled fields (renderMd/escHtml) before assembling HTML (see dashboard/js/charter-editor.js)
|
|
727
|
+
cell.insertAdjacentHTML('beforeend', html);
|
|
728
|
+
cell.dataset.loaded = '1';
|
|
729
|
+
} catch (err) {
|
|
730
|
+
cell.textContent = 'Failed to load charter: ' + err.message;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
699
734
|
async function initRuntimeFleetUI(engineCfg, agentsCfg) {
|
|
700
735
|
const cliSelect = document.getElementById('set-defaultCli');
|
|
701
736
|
const ccCliSelect = document.getElementById('set-ccCli');
|
package/dashboard/slim/body.html
CHANGED
|
@@ -75,6 +75,11 @@
|
|
|
75
75
|
<div class="member-grid" id="member-grid">
|
|
76
76
|
<div class="member-empty">Loading team…</div>
|
|
77
77
|
</div>
|
|
78
|
+
<!-- Inline hover details bar: sweeping across the member cards reveals
|
|
79
|
+
this bar with the hovered agent's task + a live working-for
|
|
80
|
+
duration, without opening the click->modal. Populated by
|
|
81
|
+
showAgentHoverBar(); hidden until first hover/focus. -->
|
|
82
|
+
<div class="member-hover-bar" id="member-hover-bar" hidden></div>
|
|
78
83
|
</div>
|
|
79
84
|
<!-- System sub-section: cockpit indicator tiles. The per-minion "working"
|
|
80
85
|
count now lives in the Team cards above, so that tile is removed. -->
|
|
@@ -47,6 +47,13 @@
|
|
|
47
47
|
card.addEventListener('keydown', function(ev) {
|
|
48
48
|
if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); openAgentDetail(a); }
|
|
49
49
|
});
|
|
50
|
+
// Hover (and keyboard focus, for tabindex=0 parity) reveals the inline
|
|
51
|
+
// details bar under the grid without opening the click->modal. Listeners
|
|
52
|
+
// are re-attached on every renderMembers since the cards are recreated.
|
|
53
|
+
card.addEventListener('mouseenter', function() { showAgentHoverBar(a); });
|
|
54
|
+
card.addEventListener('mouseleave', _scheduleHideAgentHoverBar);
|
|
55
|
+
card.addEventListener('focus', function() { showAgentHoverBar(a); });
|
|
56
|
+
card.addEventListener('blur', _scheduleHideAgentHoverBar);
|
|
50
57
|
|
|
51
58
|
var emoji = document.createElement('div');
|
|
52
59
|
emoji.className = 'member-emoji';
|
|
@@ -92,6 +99,111 @@
|
|
|
92
99
|
if (_agentDetailTimer) { clearInterval(_agentDetailTimer); _agentDetailTimer = null; }
|
|
93
100
|
}
|
|
94
101
|
|
|
102
|
+
// ── Inline hover details bar (member grid) ──────────────────────────
|
|
103
|
+
// Sweeping across .member-card elements reveals an inline bar UNDER the grid
|
|
104
|
+
// showing the hovered agent's task + a live "Working for" duration — without
|
|
105
|
+
// opening the click->modal. Uses a hover-scoped ticker distinct from the
|
|
106
|
+
// modal's _agentDetailTimer so the two never fight over one interval, and is
|
|
107
|
+
// cleared on every enter/leave/swap so no interval leaks when sweeping
|
|
108
|
+
// quickly across many cards.
|
|
109
|
+
var _agentHoverTimer = null;
|
|
110
|
+
|
|
111
|
+
function _tickAgentHoverRuntime() {
|
|
112
|
+
var el = document.getElementById('slim-agent-hover-tick');
|
|
113
|
+
if (!el) { _stopAgentHoverRuntime(); return; }
|
|
114
|
+
el.textContent = _fmtAgentElapsed(Date.now() - new Date(el.dataset.started).getTime());
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function _stopAgentHoverRuntime() {
|
|
118
|
+
if (_agentHoverTimer) { clearInterval(_agentHoverTimer); _agentHoverTimer = null; }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Dismiss-grace delay: the hover bar is hidden 0.5s after the pointer leaves
|
|
122
|
+
// (or focus blurs) rather than instantly, so brushing past a card or hopping
|
|
123
|
+
// between agents doesn't make the bottom status flicker. Re-entering any card
|
|
124
|
+
// cancels the pending hide (showAgentHoverBar → _cancelPendingHoverHide).
|
|
125
|
+
var AGENT_HOVER_DISMISS_MS = 500;
|
|
126
|
+
var _agentHoverHideTimer = null;
|
|
127
|
+
|
|
128
|
+
function _cancelPendingHoverHide() {
|
|
129
|
+
if (_agentHoverHideTimer) { clearTimeout(_agentHoverHideTimer); _agentHoverHideTimer = null; }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function _scheduleHideAgentHoverBar() {
|
|
133
|
+
_cancelPendingHoverHide();
|
|
134
|
+
_agentHoverHideTimer = setTimeout(function() {
|
|
135
|
+
_agentHoverHideTimer = null;
|
|
136
|
+
hideAgentHoverBar();
|
|
137
|
+
}, AGENT_HOVER_DISMISS_MS);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Populate + reveal the hover bar for the given agent object (passed straight
|
|
141
|
+
// from the hovered/focused card). Always clears any prior hover ticker first
|
|
142
|
+
// so swapping between cards can never leak a second interval.
|
|
143
|
+
function showAgentHoverBar(a) {
|
|
144
|
+
var bar = document.getElementById('member-hover-bar');
|
|
145
|
+
if (!a || !bar) return;
|
|
146
|
+
_cancelPendingHoverHide();
|
|
147
|
+
_stopAgentHoverRuntime();
|
|
148
|
+
bar.textContent = '';
|
|
149
|
+
|
|
150
|
+
var name = document.createElement('span');
|
|
151
|
+
name.className = 'member-hover-name';
|
|
152
|
+
name.textContent = a.name || a.id;
|
|
153
|
+
bar.appendChild(name);
|
|
154
|
+
|
|
155
|
+
// Reuse the colored member-status chip rather than re-deriving status copy.
|
|
156
|
+
bar.appendChild(createStatusChip(a.status));
|
|
157
|
+
|
|
158
|
+
var runtime = [a.runtime, a.model].filter(Boolean).join(' · ');
|
|
159
|
+
if (runtime) {
|
|
160
|
+
var rt = document.createElement('span');
|
|
161
|
+
rt.className = 'member-hover-runtime';
|
|
162
|
+
rt.textContent = runtime;
|
|
163
|
+
bar.appendChild(rt);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Only running agents with a start timestamp get the live elapsed segment.
|
|
167
|
+
// Rendered right after the runtime/model so the model name sits next to
|
|
168
|
+
// "Working for"; the work item then takes its own full-width row below.
|
|
169
|
+
if (a.status === 'working' && a.started_at) {
|
|
170
|
+
var work = document.createElement('span');
|
|
171
|
+
work.className = 'member-hover-working';
|
|
172
|
+
var label = document.createElement('span');
|
|
173
|
+
label.className = 'member-hover-working-label';
|
|
174
|
+
label.textContent = 'Working for ';
|
|
175
|
+
var tick = document.createElement('span');
|
|
176
|
+
tick.id = 'slim-agent-hover-tick';
|
|
177
|
+
tick.dataset.started = a.started_at;
|
|
178
|
+
work.appendChild(label);
|
|
179
|
+
work.appendChild(tick);
|
|
180
|
+
bar.appendChild(work);
|
|
181
|
+
_tickAgentHoverRuntime();
|
|
182
|
+
_agentHoverTimer = setInterval(_tickAgentHoverRuntime, 1000);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Work item (current task) appended last so it wraps onto its own
|
|
186
|
+
// full-width row beneath the name/status/model/working-for metadata.
|
|
187
|
+
var task = a.currentTask || a.lastAction || '';
|
|
188
|
+
var taskEl = document.createElement('span');
|
|
189
|
+
taskEl.className = 'member-hover-task' + (task ? '' : ' muted');
|
|
190
|
+
taskEl.textContent = task || 'Idle — nothing in flight';
|
|
191
|
+
bar.appendChild(taskEl);
|
|
192
|
+
|
|
193
|
+
bar.classList.add('open');
|
|
194
|
+
bar.removeAttribute('hidden');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Hide the hover bar and clear its ticker (no leaked intervals on leave/blur).
|
|
198
|
+
function hideAgentHoverBar() {
|
|
199
|
+
_cancelPendingHoverHide();
|
|
200
|
+
_stopAgentHoverRuntime();
|
|
201
|
+
var bar = document.getElementById('member-hover-bar');
|
|
202
|
+
if (!bar) return;
|
|
203
|
+
bar.classList.remove('open');
|
|
204
|
+
bar.setAttribute('hidden', '');
|
|
205
|
+
}
|
|
206
|
+
|
|
95
207
|
// Append a labelled key/value row to the agent detail modal body.
|
|
96
208
|
function appendAgentRow(body, key, value, muted) {
|
|
97
209
|
var row = document.createElement('div');
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// Project context picker: populated from /api/status (which already returns
|
|
2
|
-
// the configured project list). One-or-more projects: a
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// add one. No auto-default: we never silently fall back to projects[0] /
|
|
2
|
+
// the configured project list). One-or-more projects: a row of selectable pill
|
|
3
|
+
// buttons, single-select, none active until the user explicitly picks one and
|
|
4
|
+
// can always be toggled back off to a "Select project" state. Zero-project: a
|
|
5
|
+
// hint to add one. No auto-default: we never silently fall back to projects[0] /
|
|
7
6
|
// last-used / constellation, so project-scoped CC actions can't land in the
|
|
8
7
|
// wrong project (W-mqayzsj3). Selection persists per-browser in localStorage
|
|
9
8
|
// and is included in every /api/command-center/stream call.
|
|
@@ -26,29 +25,33 @@
|
|
|
26
25
|
empty.textContent = 'No projects linked yet.';
|
|
27
26
|
return empty;
|
|
28
27
|
}
|
|
29
|
-
// Strip variant: project
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
sel.appendChild(placeholder);
|
|
28
|
+
// Strip variant: project pills. One selectable pill per project, single-select.
|
|
29
|
+
// No auto-default: when nothing is explicitly chosen, no pill is active and the
|
|
30
|
+
// indicator stays in the "Select project" state, so the picker never silently
|
|
31
|
+
// falls back to projects[0] (W-mqayzsj3) and the user can always toggle their
|
|
32
|
+
// choice back off. Returns a container of pill buttons built as real DOM nodes
|
|
33
|
+
// (no innerHTML — SEC-03 ratchet, see test/unit.test.js DYNAMIC_INNERHTML_BASELINE);
|
|
34
|
+
// the caller attaches a click listener via event delegation.
|
|
35
|
+
function makeContextPills(projects, selected, displayByName) {
|
|
36
|
+
var group = document.createElement('span');
|
|
37
|
+
group.id = 'chat-context-pills';
|
|
38
|
+
group.className = 'context-pills';
|
|
39
|
+
group.setAttribute('role', 'radiogroup');
|
|
40
|
+
group.setAttribute('aria-label', 'Working in project');
|
|
43
41
|
for (var i = 0; i < projects.length; i++) {
|
|
44
42
|
var p = projects[i];
|
|
45
|
-
var
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
var pill = document.createElement('button');
|
|
44
|
+
pill.type = 'button';
|
|
45
|
+
pill.className = 'context-pill';
|
|
46
|
+
pill.dataset.project = p;
|
|
47
|
+
pill.textContent = (displayByName && displayByName[p]) || p;
|
|
48
|
+
pill.setAttribute('role', 'radio');
|
|
49
|
+
var isActive = p === selected;
|
|
50
|
+
pill.classList.toggle('active', isActive);
|
|
51
|
+
pill.setAttribute('aria-checked', isActive ? 'true' : 'false');
|
|
52
|
+
group.appendChild(pill);
|
|
50
53
|
}
|
|
51
|
-
return
|
|
54
|
+
return group;
|
|
52
55
|
}
|
|
53
56
|
async function loadProjectsAndRenderContext(opts) {
|
|
54
57
|
var stripEl = document.getElementById('chat-context-strip');
|
|
@@ -77,23 +80,33 @@
|
|
|
77
80
|
try { localStorage.removeItem(SLIM_PROJECT_KEY); } catch (_e) {}
|
|
78
81
|
}
|
|
79
82
|
// No auto-default: when nothing is explicitly selected, currentProject
|
|
80
|
-
// stays null and
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
var
|
|
83
|
+
// stays null and no pill is active (the "Select project" state). We
|
|
84
|
+
// deliberately do NOT fall back to projects[0] here (W-mqayzsj3) so CC
|
|
85
|
+
// turns and project-scoped actions don't silently target the wrong
|
|
86
|
+
// project.
|
|
87
|
+
var pills = makeContextPills(projects, currentProject, displayByName);
|
|
85
88
|
stripEl.style.display = '';
|
|
86
|
-
controlsEl.replaceChildren(
|
|
87
|
-
|
|
88
|
-
var
|
|
89
|
-
if (
|
|
89
|
+
controlsEl.replaceChildren(pills, makeContextAddBtn());
|
|
90
|
+
pills.addEventListener('click', function(ev) {
|
|
91
|
+
var pill = ev.target.closest('.context-pill');
|
|
92
|
+
if (!pill || !pills.contains(pill)) return;
|
|
93
|
+
var val = pill.dataset.project || '';
|
|
94
|
+
if (val && val !== currentProject) {
|
|
95
|
+
// Select this project.
|
|
90
96
|
currentProject = val;
|
|
91
97
|
try { localStorage.setItem(SLIM_PROJECT_KEY, currentProject); } catch (_e) {}
|
|
92
98
|
} else {
|
|
93
|
-
//
|
|
99
|
+
// Re-clicking the active pill toggles back to "Select project" —
|
|
100
|
+
// drop the persisted choice so nothing is selected.
|
|
94
101
|
currentProject = null;
|
|
95
102
|
try { localStorage.removeItem(SLIM_PROJECT_KEY); } catch (_e) {}
|
|
96
103
|
}
|
|
104
|
+
var nodes = pills.querySelectorAll('.context-pill');
|
|
105
|
+
for (var j = 0; j < nodes.length; j++) {
|
|
106
|
+
var active = nodes[j].dataset.project === currentProject;
|
|
107
|
+
nodes[j].classList.toggle('active', active);
|
|
108
|
+
nodes[j].setAttribute('aria-checked', active ? 'true' : 'false');
|
|
109
|
+
}
|
|
97
110
|
});
|
|
98
111
|
}
|
|
99
112
|
var addBtn = document.getElementById('chat-context-add');
|
|
@@ -496,27 +496,54 @@
|
|
|
496
496
|
font-weight: 700;
|
|
497
497
|
font-size: var(--text-base);
|
|
498
498
|
}
|
|
499
|
-
|
|
499
|
+
/* Pills + "+ Add" lay out in a single row next to the "Working in" label
|
|
500
|
+
(W-mqsdptzy / PR #366). #chat-context-controls is a plain <span>, so
|
|
501
|
+
without an explicit flex the display:flex .context-pills (a block-level
|
|
502
|
+
flex container) pushed the "+ Add" sibling onto its own line below. */
|
|
503
|
+
.chat-context-strip #chat-context-controls {
|
|
504
|
+
display: flex;
|
|
505
|
+
align-items: center;
|
|
506
|
+
gap: var(--space-2);
|
|
507
|
+
flex-wrap: wrap;
|
|
508
|
+
min-width: 0;
|
|
509
|
+
}
|
|
510
|
+
.chat-context-strip .context-pills {
|
|
511
|
+
display: flex;
|
|
512
|
+
align-items: center;
|
|
513
|
+
gap: var(--space-2);
|
|
514
|
+
flex-wrap: wrap;
|
|
515
|
+
}
|
|
516
|
+
/* Project pills: outlined by default, filled/highlighted when active.
|
|
517
|
+
Single-select — exactly one (or zero) carries .active at a time. */
|
|
518
|
+
.chat-context-strip .context-pill {
|
|
500
519
|
background: var(--bg);
|
|
501
520
|
color: var(--text);
|
|
502
521
|
border: 1px solid var(--border);
|
|
503
|
-
border-radius:
|
|
504
|
-
padding: 4px
|
|
522
|
+
border-radius: 999px;
|
|
523
|
+
padding: 4px 12px;
|
|
505
524
|
font-family: inherit;
|
|
506
525
|
font-size: var(--text-base);
|
|
526
|
+
line-height: 1;
|
|
507
527
|
cursor: pointer;
|
|
528
|
+
transition: background 0.12s, border-color 0.12s, color 0.12s;
|
|
529
|
+
}
|
|
530
|
+
.chat-context-strip .context-pill:hover {
|
|
531
|
+
border-color: var(--blue);
|
|
532
|
+
background: var(--surface2);
|
|
533
|
+
}
|
|
534
|
+
.chat-context-strip .context-pill:focus-visible {
|
|
535
|
+
outline: none;
|
|
536
|
+
border-color: var(--blue);
|
|
537
|
+
}
|
|
538
|
+
.chat-context-strip .context-pill.active {
|
|
539
|
+
background: var(--blue);
|
|
540
|
+
border-color: var(--blue);
|
|
541
|
+
color: #fff;
|
|
542
|
+
font-weight: 600;
|
|
543
|
+
}
|
|
544
|
+
.chat-context-strip .context-pill.active:hover {
|
|
545
|
+
background: var(--blue);
|
|
508
546
|
}
|
|
509
|
-
.chat-context-strip select:focus { outline: none; border-color: var(--blue); }
|
|
510
|
-
/* Italicize the "Select project" placeholder option AND the closed select
|
|
511
|
-
widget while the placeholder is the active value. `:has()` is needed
|
|
512
|
-
because native <select> rendering ignores child <option> styles for the
|
|
513
|
-
displayed (closed) text — we have to style the <select> itself. The
|
|
514
|
-
explicit `option { font-style: normal }` reset stops real project names
|
|
515
|
-
from inheriting the parent select's italic when the dropdown is opened
|
|
516
|
-
while the placeholder is still selected. */
|
|
517
|
-
.chat-context-strip select option { font-style: normal; color: var(--text); }
|
|
518
|
-
.chat-context-strip select option[value=""] { font-style: italic; color: var(--muted); }
|
|
519
|
-
.chat-context-strip select:has(option[value=""]:checked) { font-style: italic; color: var(--muted); }
|
|
520
547
|
.chat-context-strip .context-static {
|
|
521
548
|
color: var(--text);
|
|
522
549
|
font-weight: 500;
|
|
@@ -1204,6 +1231,37 @@
|
|
|
1204
1231
|
50% { opacity: 0.55; }
|
|
1205
1232
|
}
|
|
1206
1233
|
|
|
1234
|
+
/* Inline hover/focus details bar under the member grid. Sweeping across the
|
|
1235
|
+
cards reveals the hovered agent's task + live working-for duration
|
|
1236
|
+
without opening the click->modal; harmonizes with the .member-card and
|
|
1237
|
+
.agent-detail-* surfaces. */
|
|
1238
|
+
.member-hover-bar {
|
|
1239
|
+
margin-top: 8px;
|
|
1240
|
+
display: flex;
|
|
1241
|
+
flex-wrap: wrap;
|
|
1242
|
+
align-items: center;
|
|
1243
|
+
gap: 8px;
|
|
1244
|
+
padding: 8px 10px;
|
|
1245
|
+
background: var(--surface2);
|
|
1246
|
+
border: 1px solid var(--border);
|
|
1247
|
+
border-radius: var(--radius);
|
|
1248
|
+
font-size: var(--text-md);
|
|
1249
|
+
}
|
|
1250
|
+
.member-hover-bar[hidden] { display: none; }
|
|
1251
|
+
.member-hover-name { font-weight: 600; color: var(--text); }
|
|
1252
|
+
.member-hover-task {
|
|
1253
|
+
flex: 1 1 100%;
|
|
1254
|
+
min-width: 0;
|
|
1255
|
+
color: var(--text);
|
|
1256
|
+
overflow: hidden;
|
|
1257
|
+
text-overflow: ellipsis;
|
|
1258
|
+
white-space: nowrap;
|
|
1259
|
+
}
|
|
1260
|
+
.member-hover-task.muted { color: var(--muted); font-style: italic; }
|
|
1261
|
+
.member-hover-runtime { font-size: var(--text-base); color: var(--muted); }
|
|
1262
|
+
.member-hover-working { white-space: nowrap; color: var(--amber); font-weight: 600; }
|
|
1263
|
+
.member-hover-working-label { color: var(--muted); font-weight: 400; }
|
|
1264
|
+
|
|
1207
1265
|
/* Agent detail modal body */
|
|
1208
1266
|
.agent-detail-head {
|
|
1209
1267
|
display: flex;
|
package/dashboard-build.js
CHANGED
|
@@ -44,7 +44,7 @@ function buildDashboardHtml() {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const jsFiles = [
|
|
47
|
-
'utils', 'state', 'features-client', 'render-utils', 'detail-panel', 'live-stream',
|
|
47
|
+
'utils', 'state', 'features-client', 'render-utils', 'charter-editor', 'detail-panel', 'live-stream',
|
|
48
48
|
'render-agents', 'render-dispatch', 'render-work-items', 'render-prd',
|
|
49
49
|
'render-prs', 'render-plans', 'render-inbox', 'render-kb', 'render-skills',
|
|
50
50
|
'render-other', 'render-managed', 'memory-panel', 'render-schedules', 'render-watches', 'render-pipelines', 'render-meetings', 'render-pinned',
|
package/dashboard.js
CHANGED
|
@@ -1798,7 +1798,7 @@ function buildDashboardHtml() {
|
|
|
1798
1798
|
|
|
1799
1799
|
// Assemble JS modules (order matters: utils → state → renderers → commands → refresh)
|
|
1800
1800
|
const jsFiles = [
|
|
1801
|
-
'utils', 'state', 'features-client', 'render-utils', 'detail-panel', 'live-stream',
|
|
1801
|
+
'utils', 'state', 'features-client', 'render-utils', 'charter-editor', 'detail-panel', 'live-stream',
|
|
1802
1802
|
'render-agents', 'render-dispatch', 'render-work-items', 'render-prd',
|
|
1803
1803
|
'render-prs', 'render-plans', 'render-inbox', 'render-kb', 'render-skills',
|
|
1804
1804
|
'render-other', 'render-managed', 'render-schedules', 'render-watches', 'render-pipelines', 'render-meetings', 'render-pinned',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2257",
|
|
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"
|