@yemi33/minions 0.1.2388 → 0.1.2390
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/render-plans.js +1 -1
- package/dashboard/js/settings.js +30 -0
- package/dashboard/slim/body.html +7 -4
- package/dashboard/slim/js/settings.js +33 -92
- package/dashboard/slim/styles.css +8 -27
- package/dashboard/styles.css +15 -0
- package/engine/check-status.js +2 -4
- package/engine/consolidation.js +1 -1
- package/engine/queries.js +2 -2
- package/package.json +1 -1
|
@@ -627,7 +627,7 @@ function _renderPlanModal(normalizedFile, raw, lastMod) {
|
|
|
627
627
|
text = 'Project: ' + (plan.project || '?') +
|
|
628
628
|
'\nStrategy: ' + (plan.branch_strategy || 'parallel') +
|
|
629
629
|
'\nBranch: ' + (plan.feature_branch || 'per-item') +
|
|
630
|
-
'\nStatus: ' + (plan.status || 'active') +
|
|
630
|
+
'\nStatus: ' + (plan.status || (plan.requires_approval && !plan.approvedAt ? 'awaiting-approval' : 'active')) +
|
|
631
631
|
'\nGenerated by: ' + (plan.generated_by || '?') + ' on ' + (plan.generated_at || '?') +
|
|
632
632
|
'\n\n--- Items (' + (plan.missing_features || []).length + ') ---\n\n' + items +
|
|
633
633
|
(plan.open_questions?.length ? '\n\n--- Open Questions ---\n\n' + plan.open_questions.map(q => '\u2022 ' + q).join('\n') : '');
|
package/dashboard/js/settings.js
CHANGED
|
@@ -1445,3 +1445,33 @@ async function removeProject(name) {
|
|
|
1445
1445
|
}
|
|
1446
1446
|
|
|
1447
1447
|
window.MinionsSettings = { openSettings, saveSettings, addProject, removeProject, resetSettingsToDefaults, _selectSettingsTab, _applySettingsSearch };
|
|
1448
|
+
|
|
1449
|
+
// ── Settings embed mode (Slim UX parity — W-mrkpke07000i8ed5) ───────────
|
|
1450
|
+
// The Slim UX Settings modal embeds this LITERAL classic Settings surface in
|
|
1451
|
+
// an iframe at /settings?embed=1 (chrome-off) so there is ONE settings UI, not
|
|
1452
|
+
// two (CLAUDE.md "Slim UX vs Classic UX — reuse, don't fork"; mirrors the
|
|
1453
|
+
// Queued-work/Plans/PRs cockpit tiles). Slim and classic are separate IIFE
|
|
1454
|
+
// bundles with no shared runtime scope, so slim can't call openSettings()
|
|
1455
|
+
// in-process — it iframes the real classic screen instead. On boot, when we
|
|
1456
|
+
// ARE the embedded /settings page, auto-open the modal and tag <html> so the
|
|
1457
|
+
// embed CSS renders the settings modal full-bleed inside the iframe. Only the
|
|
1458
|
+
// /settings path triggers this; every other embedded page (/work, /plans,
|
|
1459
|
+
// /prs) is untouched.
|
|
1460
|
+
(function bootSettingsEmbed() {
|
|
1461
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
|
1462
|
+
try {
|
|
1463
|
+
var isSettingsPath = window.location.pathname.replace(/\/+$/, '') === '/settings';
|
|
1464
|
+
var isEmbed = /[?&]embed=1(?:&|$)/.test(window.location.search);
|
|
1465
|
+
if (!isSettingsPath || !isEmbed) return;
|
|
1466
|
+
} catch (e) { return; }
|
|
1467
|
+
try { document.documentElement.classList.add('embed-settings'); } catch (e) { /* no DOM attr */ }
|
|
1468
|
+
function openWhenReady() {
|
|
1469
|
+
if (typeof openSettings === 'function') { openSettings(); return; }
|
|
1470
|
+
setTimeout(openWhenReady, 30); // wait for the settings module + deps to register
|
|
1471
|
+
}
|
|
1472
|
+
if (document.readyState === 'loading') {
|
|
1473
|
+
document.addEventListener('DOMContentLoaded', function() { setTimeout(openWhenReady, 0); });
|
|
1474
|
+
} else {
|
|
1475
|
+
setTimeout(openWhenReady, 0);
|
|
1476
|
+
}
|
|
1477
|
+
})();
|
package/dashboard/slim/body.html
CHANGED
|
@@ -270,16 +270,19 @@
|
|
|
270
270
|
</div>
|
|
271
271
|
</div>
|
|
272
272
|
|
|
273
|
-
<!-- Settings dialog
|
|
274
|
-
|
|
273
|
+
<!-- Settings dialog — embeds the LITERAL classic dashboard Settings surface
|
|
274
|
+
in an iframe (/settings?embed=1) so slim shows the FULL settings (engine
|
|
275
|
+
toggles, runtime/model, projects, agents, routing, feature flags incl.
|
|
276
|
+
slim-ux) with zero forked rendering. See dashboard/slim/js/settings.js
|
|
277
|
+
(W-mrkpke07000i8ed5 / "reuse, don't fork"). -->
|
|
275
278
|
<div class="modal-bg" id="slim-settings-modal">
|
|
276
279
|
<div class="modal">
|
|
277
280
|
<div class="modal-header">
|
|
278
|
-
<h3>Settings
|
|
281
|
+
<h3>Settings</h3>
|
|
279
282
|
<button id="slim-settings-close" class="icon-btn" title="Close">×</button>
|
|
280
283
|
</div>
|
|
281
284
|
<div class="modal-body" id="slim-settings-body">
|
|
282
|
-
<p>Loading
|
|
285
|
+
<p>Loading settings…</p>
|
|
283
286
|
</div>
|
|
284
287
|
</div>
|
|
285
288
|
</div>
|
|
@@ -1,80 +1,39 @@
|
|
|
1
1
|
// ── Settings dialog ────────────────────────────────────────────
|
|
2
|
-
|
|
2
|
+
// Reuses the LITERAL classic dashboard Settings surface instead of a
|
|
3
|
+
// slim-only feature-flag list, so there is ONE settings UI, not two
|
|
4
|
+
// (W-mrkpke07000i8ed5 / CLAUDE.md "Slim UX vs Classic UX — reuse, don't
|
|
5
|
+
// fork"; mirrors the Queued-work/Plans/PRs cockpit tiles). Slim and classic
|
|
6
|
+
// are two separate IIFE bundles with no shared runtime scope, so we can't
|
|
7
|
+
// call the classic openSettings() in-process; instead we embed the real
|
|
8
|
+
// classic /settings screen in an iframe with the chrome-off ?embed=1 mode.
|
|
9
|
+
// The iframed page IS the classic Settings modal — every engine toggle,
|
|
10
|
+
// runtime/model, projects, agents, routing, feature flags (incl. slim-ux) —
|
|
11
|
+
// backed by the same /api/settings + /api/features endpoints, with zero
|
|
12
|
+
// duplicated rendering logic. Classic is reachable at /settings even with
|
|
13
|
+
// slim-ux ON (only / is taken over). Built with createElement (no innerHTML)
|
|
14
|
+
// to satisfy the dashboard no-unsanitized lint gate.
|
|
15
|
+
function openSlimSettings() {
|
|
3
16
|
var modal = document.getElementById('slim-settings-modal');
|
|
4
17
|
var body = document.getElementById('slim-settings-body');
|
|
5
|
-
|
|
18
|
+
if (!modal || !body) return;
|
|
19
|
+
body.textContent = '';
|
|
20
|
+
var frame = document.createElement('iframe');
|
|
21
|
+
frame.className = 'slim-settings-embed';
|
|
22
|
+
frame.src = '/settings?embed=1';
|
|
23
|
+
frame.title = 'Settings';
|
|
24
|
+
frame.addEventListener('load', function() {
|
|
25
|
+
var frameDocument = frame.contentDocument;
|
|
26
|
+
if (!frameDocument) return;
|
|
27
|
+
// Capture before the classic modal handler so only the parent modal closes.
|
|
28
|
+
frameDocument.addEventListener('keydown', function(ev) {
|
|
29
|
+
if (ev.key !== 'Escape' && ev.keyCode !== 27) return;
|
|
30
|
+
ev.preventDefault();
|
|
31
|
+
ev.stopImmediatePropagation();
|
|
32
|
+
closeSlimSettings();
|
|
33
|
+
}, true);
|
|
34
|
+
});
|
|
35
|
+
body.appendChild(frame);
|
|
6
36
|
modal.classList.add('open');
|
|
7
|
-
try {
|
|
8
|
-
var res = await fetch('/api/features');
|
|
9
|
-
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
10
|
-
var data = await res.json();
|
|
11
|
-
var flags = (data && data.features) || [];
|
|
12
|
-
|
|
13
|
-
// Build the body as a document fragment so we avoid .innerHTML for
|
|
14
|
-
// the dynamic parts (SEC-03 ratchet — see DYNAMIC_INNERHTML_BASELINE
|
|
15
|
-
// in test/unit.test.js). Static intro paragraph uses innerHTML
|
|
16
|
-
// because it carries a <code> tag and is a pure string literal
|
|
17
|
-
// (exempted by _isStaticInnerHtmlRhs).
|
|
18
|
-
var intro = document.createElement('p');
|
|
19
|
-
intro.innerHTML = 'Toggle <code>slim-ux</code> off to return to the original dashboard. Advanced settings (agents, projects, runtime) live there for now.';
|
|
20
|
-
var frag = document.createDocumentFragment();
|
|
21
|
-
frag.appendChild(intro);
|
|
22
|
-
|
|
23
|
-
if (flags.length === 0) {
|
|
24
|
-
var none = document.createElement('p');
|
|
25
|
-
none.innerHTML = 'No experimental flags registered. Add entries to <code>engine/features.js</code>.';
|
|
26
|
-
frag.appendChild(none);
|
|
27
|
-
} else {
|
|
28
|
-
for (var i = 0; i < flags.length; i++) {
|
|
29
|
-
var f = flags[i];
|
|
30
|
-
var row = document.createElement('div');
|
|
31
|
-
row.className = 'flag-row';
|
|
32
|
-
var info = document.createElement('div');
|
|
33
|
-
var name = document.createElement('div');
|
|
34
|
-
name.className = 'flag-name';
|
|
35
|
-
name.textContent = f.id;
|
|
36
|
-
var desc = document.createElement('div');
|
|
37
|
-
desc.className = 'flag-desc';
|
|
38
|
-
desc.textContent = f.description || '';
|
|
39
|
-
info.appendChild(name);
|
|
40
|
-
info.appendChild(desc);
|
|
41
|
-
var lbl = document.createElement('label');
|
|
42
|
-
lbl.className = 'flag-toggle';
|
|
43
|
-
var input = document.createElement('input');
|
|
44
|
-
input.type = 'checkbox';
|
|
45
|
-
input.setAttribute('data-flag', f.id);
|
|
46
|
-
if (f.enabled) input.checked = true;
|
|
47
|
-
var stateSpan = document.createElement('span');
|
|
48
|
-
stateSpan.textContent = f.enabled ? 'On' : 'Off';
|
|
49
|
-
lbl.appendChild(input);
|
|
50
|
-
lbl.appendChild(stateSpan);
|
|
51
|
-
row.appendChild(info);
|
|
52
|
-
row.appendChild(lbl);
|
|
53
|
-
frag.appendChild(row);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// TEMP: deep-link to the full dashboard's settings page. The slim
|
|
58
|
-
// intentionally doesn't reimplement that whole UI — Carlos asked us
|
|
59
|
-
// to keep settings sprawl off the main slim view.
|
|
60
|
-
var link = document.createElement('a');
|
|
61
|
-
link.className = 'settings-link';
|
|
62
|
-
link.href = '/?fullDashboard=1';
|
|
63
|
-
link.title = 'Open the full dashboard’s settings page';
|
|
64
|
-
link.textContent = 'Advanced settings → full dashboard';
|
|
65
|
-
frag.appendChild(link);
|
|
66
|
-
|
|
67
|
-
body.replaceChildren(frag);
|
|
68
|
-
var inputs = body.querySelectorAll('input[type=checkbox][data-flag]');
|
|
69
|
-
for (var j = 0; j < inputs.length; j++) {
|
|
70
|
-
inputs[j].addEventListener('change', onFlagToggle);
|
|
71
|
-
}
|
|
72
|
-
} catch (e) {
|
|
73
|
-
var err = document.createElement('p');
|
|
74
|
-
err.style.color = 'var(--red)';
|
|
75
|
-
err.textContent = 'Failed to load flags: ' + (e && e.message ? e.message : 'unknown error');
|
|
76
|
-
body.replaceChildren(err);
|
|
77
|
-
}
|
|
78
37
|
}
|
|
79
38
|
|
|
80
39
|
function closeSlimSettings() {
|
|
@@ -99,7 +58,7 @@
|
|
|
99
58
|
})();
|
|
100
59
|
|
|
101
60
|
// POST a feature-flag toggle; throws on a non-2xx so callers can branch on
|
|
102
|
-
// failure.
|
|
61
|
+
// failure. Used by the one-click returnToClassicDashboard().
|
|
103
62
|
async function postFeatureToggle(id, enabled) {
|
|
104
63
|
var res = await fetch('/api/features/toggle', {
|
|
105
64
|
method: 'POST',
|
|
@@ -223,24 +182,6 @@
|
|
|
223
182
|
bindModalClose('slim-bug-modal', 'slim-bug-close');
|
|
224
183
|
})();
|
|
225
184
|
|
|
226
|
-
async function onFlagToggle(ev) {
|
|
227
|
-
var input = ev.currentTarget;
|
|
228
|
-
var id = input.getAttribute('data-flag');
|
|
229
|
-
var enabled = input.checked;
|
|
230
|
-
var label = input.parentElement.querySelector('span');
|
|
231
|
-
if (label) label.textContent = enabled ? 'On' : 'Off';
|
|
232
|
-
try {
|
|
233
|
-
await postFeatureToggle(id, enabled);
|
|
234
|
-
if (id === 'slim-ux' && !enabled) {
|
|
235
|
-
location.reload();
|
|
236
|
-
}
|
|
237
|
-
} catch (e) {
|
|
238
|
-
input.checked = !enabled;
|
|
239
|
-
if (label) label.textContent = !enabled ? 'On' : 'Off';
|
|
240
|
-
alert('Toggle failed: ' + e.message);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
185
|
// ── Action prompt modal (TEMP: routes through chat) ──────────────
|
|
245
186
|
// Each of the 4 action buttons opens this modal with prompt text
|
|
246
187
|
// tailored to the concept. On submit we drop the user's text into the
|
|
@@ -690,9 +690,16 @@
|
|
|
690
690
|
#slim-tile-modal.tile-modal--work .modal-body,
|
|
691
691
|
#slim-tile-modal.tile-modal--plans .modal-body,
|
|
692
692
|
#slim-tile-modal.tile-modal--prs .modal-body { padding: 0; }
|
|
693
|
+
/* W-mrkpke07000i8ed5 — the Settings modal embeds the full classic Settings
|
|
694
|
+
screen (/settings?embed=1) in an iframe; widen the modal + drop the body
|
|
695
|
+
padding so the embedded settings surface (left rail + content) gets the
|
|
696
|
+
full width/height. Mirrors the Queued-work/Plans/PRs tile treatment. */
|
|
697
|
+
#slim-settings-modal .modal { width: 1180px; max-width: calc(100vw - 32px); }
|
|
698
|
+
#slim-settings-modal .modal-body { padding: 0; }
|
|
693
699
|
.slim-work-embed,
|
|
694
700
|
.slim-plans-embed,
|
|
695
|
-
.slim-prs-embed
|
|
701
|
+
.slim-prs-embed,
|
|
702
|
+
.slim-settings-embed {
|
|
696
703
|
display: block; width: 100%; height: calc(100vh - 160px); min-height: 360px;
|
|
697
704
|
border: 0; background: var(--bg);
|
|
698
705
|
}
|
|
@@ -1705,32 +1712,6 @@
|
|
|
1705
1712
|
}
|
|
1706
1713
|
.btn-primary:hover, .btn-secondary:hover { filter: brightness(1.1); }
|
|
1707
1714
|
|
|
1708
|
-
.flag-row {
|
|
1709
|
-
display: flex;
|
|
1710
|
-
align-items: center;
|
|
1711
|
-
justify-content: space-between;
|
|
1712
|
-
padding: 10px 0;
|
|
1713
|
-
border-bottom: 1px solid var(--border);
|
|
1714
|
-
}
|
|
1715
|
-
.flag-row:last-child { border-bottom: none; }
|
|
1716
|
-
.flag-name { font-size: var(--text-lg); font-weight: 600; }
|
|
1717
|
-
.flag-desc { font-size: var(--text-base); color: var(--muted); margin-top: 2px; }
|
|
1718
|
-
.flag-toggle { display: flex; align-items: center; gap: 8px; font-size: var(--text-md); }
|
|
1719
|
-
.flag-toggle input { cursor: pointer; }
|
|
1720
|
-
.settings-link {
|
|
1721
|
-
display: block;
|
|
1722
|
-
margin-top: 14px;
|
|
1723
|
-
padding: 10px 12px;
|
|
1724
|
-
background: var(--surface2);
|
|
1725
|
-
border: 1px solid var(--border);
|
|
1726
|
-
border-radius: var(--radius);
|
|
1727
|
-
color: var(--blue);
|
|
1728
|
-
text-decoration: none;
|
|
1729
|
-
font-size: var(--text-md);
|
|
1730
|
-
text-align: center;
|
|
1731
|
-
}
|
|
1732
|
-
.settings-link:hover { filter: brightness(1.1); }
|
|
1733
|
-
|
|
1734
1715
|
.modal-textarea {
|
|
1735
1716
|
width: 100%;
|
|
1736
1717
|
min-height: 96px;
|
package/dashboard/styles.css
CHANGED
|
@@ -1645,3 +1645,18 @@
|
|
|
1645
1645
|
base .page-layout flex row already gives .page-content full width plus a
|
|
1646
1646
|
bounded height to scroll within. */
|
|
1647
1647
|
html.embed .page-content { padding: 12px 16px; overflow-y: auto; }
|
|
1648
|
+
|
|
1649
|
+
/* W-mrkpke07000i8ed5 — settings-embed mode. The Slim UX Settings modal iframes
|
|
1650
|
+
the classic Settings surface at /settings?embed=1; settings.js#bootSettingsEmbed
|
|
1651
|
+
auto-opens the modal and tags <html class="embed-settings">. Render the
|
|
1652
|
+
settings modal full-bleed inside the iframe (no dim backdrop, no rounded
|
|
1653
|
+
card, fill the frame), hide the empty home page-content behind it, and hide
|
|
1654
|
+
the modal close X — the parent slim modal owns closing. */
|
|
1655
|
+
html.embed-settings .page-content { display: none !important; }
|
|
1656
|
+
html.embed-settings #modal.modal-bg { background: var(--bg); }
|
|
1657
|
+
html.embed-settings #modal .modal,
|
|
1658
|
+
html.embed-settings #modal .modal.modal-wide {
|
|
1659
|
+
width: 100%; max-width: none; height: 100%; max-height: none;
|
|
1660
|
+
border: none; border-radius: 0;
|
|
1661
|
+
}
|
|
1662
|
+
html.embed-settings #modal .modal-close { display: none; }
|
package/engine/check-status.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const dir = path.resolve(__dirname, '..');
|
|
4
3
|
const { safeJson, MINIONS_DIR, DONE_STATUSES } = require('./shared');
|
|
5
4
|
|
|
6
5
|
console.log('=== Work Items (non-done) ===');
|
|
@@ -12,8 +11,7 @@ items.filter(i => !DONE_STATUSES.has(i.status)).forEach(i => {
|
|
|
12
11
|
|
|
13
12
|
console.log('\n=== Agent Status (derived from dispatch) ===');
|
|
14
13
|
const { getAgentStatus } = require('./queries');
|
|
15
|
-
|
|
16
|
-
try { config = safeJson(path.join(MINIONS_DIR, 'config.json')) || {}; } catch {}
|
|
14
|
+
const config = safeJson(path.join(MINIONS_DIR, 'config.json')) || {};
|
|
17
15
|
const agents = Object.keys(config.agents || {});
|
|
18
16
|
if (agents.length === 0) {
|
|
19
17
|
console.log('(no agents configured)');
|
|
@@ -26,6 +24,6 @@ if (agents.length === 0) {
|
|
|
26
24
|
|
|
27
25
|
console.log('\n=== Inbox ===');
|
|
28
26
|
try {
|
|
29
|
-
const inbox = fs.readdirSync(path.join(
|
|
27
|
+
const inbox = fs.readdirSync(path.join(MINIONS_DIR, 'notes', 'inbox')).filter(f => f.endsWith('.md'));
|
|
30
28
|
console.log(inbox.length + ' files:', inbox.join(', '));
|
|
31
29
|
} catch { console.log('empty'); }
|
package/engine/consolidation.js
CHANGED
|
@@ -42,7 +42,7 @@ function _capNotesPreservingOverflow(newContent) {
|
|
|
42
42
|
// No section boundary before the cap — fall back to keeping header + the
|
|
43
43
|
// most recent sections, archiving the middle.
|
|
44
44
|
const sections = newContent.split('\n---\n\n### ');
|
|
45
|
-
if (sections.length >
|
|
45
|
+
if (sections.length > 9) {
|
|
46
46
|
kept = sections[0] + '\n---\n\n### ' + sections.slice(-8).join('\n---\n\n### ');
|
|
47
47
|
const middle = sections.slice(1, -8);
|
|
48
48
|
if (middle.length) dropped = '\n---\n\n### ' + middle.join('\n---\n\n### ');
|
package/engine/queries.js
CHANGED
|
@@ -2176,7 +2176,7 @@ function getPrdInfo(config) {
|
|
|
2176
2176
|
}
|
|
2177
2177
|
existingPrds.push({
|
|
2178
2178
|
file: pf,
|
|
2179
|
-
status: plan.status || 'active',
|
|
2179
|
+
status: plan.status || (plan.requires_approval && !plan.approvedAt ? 'awaiting-approval' : 'active'),
|
|
2180
2180
|
planStale: planStale || plan.planStale || false,
|
|
2181
2181
|
completedAt: plan.completedAt || '',
|
|
2182
2182
|
_archived: isArch,
|
|
@@ -2195,7 +2195,7 @@ function getPrdInfo(config) {
|
|
|
2195
2195
|
? (prdItemIdByKey.get(`${archived ? 1 : 0} ${pf} ${f.id}`) ?? null)
|
|
2196
2196
|
: null;
|
|
2197
2197
|
allPrdItems.push({
|
|
2198
|
-
...f, _source: pf, _planStatus: plan.status || 'active',
|
|
2198
|
+
...f, _source: pf, _planStatus: plan.status || (plan.requires_approval && !plan.approvedAt ? 'awaiting-approval' : 'active'),
|
|
2199
2199
|
_planSummary: plan.plan_summary || pf, _planProject: plan.project || '',
|
|
2200
2200
|
_archived: isArch, _sourcePlan: plan.source_plan || '',
|
|
2201
2201
|
_branchStrategy: plan.branch_strategy || 'parallel',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2390",
|
|
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"
|