@yemi33/minions 0.1.2413 → 0.1.2415
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/refresh.js +1 -1
- package/dashboard/js/settings.js +134 -9
- package/dashboard/styles.css +13 -0
- package/docs/dev-composite-workflow.md +101 -0
- package/package.json +1 -1
package/dashboard/js/refresh.js
CHANGED
|
@@ -185,7 +185,7 @@ const RENDER_VERSIONS = {
|
|
|
185
185
|
// Bumped to 4 by W-mqrdavob (Agents table: per-agent charter editor column).
|
|
186
186
|
// Bumped to 5 by W-mr0qs0vw (Worker Pool: CC session idle-timeout field).
|
|
187
187
|
// Bumped to 6 for the fleet ACP pool opt-in default and warning copy.
|
|
188
|
-
settings:
|
|
188
|
+
settings: 7,
|
|
189
189
|
};
|
|
190
190
|
const _sectionCache = {};
|
|
191
191
|
const _lastValueByKey = {};
|
package/dashboard/js/settings.js
CHANGED
|
@@ -713,6 +713,13 @@ async function openSettings() {
|
|
|
713
713
|
// non-dispatch toggles that historically lived under Auto-fix; placed
|
|
714
714
|
// adjacent to Auto-fix so operators following the legacy mental model find
|
|
715
715
|
// the moved flags quickly.
|
|
716
|
+
// W-mryz3r7h — a section may opt into a collapsible rail fold by declaring a
|
|
717
|
+
// `group`. Grouped sections render as an indented child list beneath a fold
|
|
718
|
+
// header (see RAIL_GROUPS + the railHtml builder below); their PANES stay in
|
|
719
|
+
// the content area unchanged. Copilot/Claude Tuning + Budget + the former
|
|
720
|
+
// standalone Advanced pane now live under the "Advanced" fold (collapsed by
|
|
721
|
+
// default). The former standalone Advanced leaf is nested here too so there is
|
|
722
|
+
// only ever one thing labeled "Advanced".
|
|
716
723
|
const sections = [
|
|
717
724
|
{ id: 'runtime', label: 'Agents Runtime & Models', featured: true, html: paneRuntime },
|
|
718
725
|
{ id: 'autofix', label: 'Auto-fix & Review Loop', featured: true, html: paneAutoFix },
|
|
@@ -723,15 +730,19 @@ async function openSettings() {
|
|
|
723
730
|
{ id: 'polling', label: 'Polling', html: panePolling },
|
|
724
731
|
{ id: 'concurrency', label: 'Concurrency & Timeouts', html: paneConcurrency },
|
|
725
732
|
{ id: 'worktree', label: 'Worker Pool & Worktrees', html: paneWorktree },
|
|
726
|
-
{ id: 'copilot', label: 'Copilot Tuning', html: paneCopilot },
|
|
727
|
-
{ id: 'claude', label: 'Claude Tuning', html: paneClaude },
|
|
728
733
|
{ id: 'harness', label: 'Runtime Harness', html: paneHarness },
|
|
729
|
-
{ id: 'budget', label: 'Budget', html: paneBudget },
|
|
730
734
|
{ id: 'maxturns', label: 'Max Turns', html: paneMaxTurns },
|
|
731
735
|
{ id: 'features', label: 'Feature Flags', html: paneFeatures },
|
|
732
|
-
{ id: '
|
|
736
|
+
{ id: 'copilot', label: 'Copilot Tuning', html: paneCopilot, group: 'advanced' },
|
|
737
|
+
{ id: 'claude', label: 'Claude Tuning', html: paneClaude, group: 'advanced' },
|
|
738
|
+
{ id: 'budget', label: 'Budget', html: paneBudget, group: 'advanced' },
|
|
739
|
+
{ id: 'advanced', label: 'Advanced', html: paneAdvanced, group: 'advanced' },
|
|
733
740
|
];
|
|
734
741
|
|
|
742
|
+
// Rail fold registry: group id → display label. Sections opt in via `group`.
|
|
743
|
+
const RAIL_GROUPS = { advanced: { label: 'Advanced' } };
|
|
744
|
+
const ADV_FOLD_KEY = 'minions.settings.advancedFoldOpen';
|
|
745
|
+
|
|
735
746
|
// localStorage-persisted active tab. Falls back to the first section when no
|
|
736
747
|
// saved value or the saved value points at a section we no longer render.
|
|
737
748
|
const SAVED_TAB_KEY = 'minions.settings.activeTab';
|
|
@@ -741,12 +752,41 @@ async function openSettings() {
|
|
|
741
752
|
if (saved && sections.some(s => s.id === saved)) activeTab = saved;
|
|
742
753
|
} catch { /* localStorage may be blocked in some embeds */ }
|
|
743
754
|
|
|
744
|
-
|
|
745
|
-
|
|
755
|
+
// Compute the Advanced fold's initial expanded state. Default (no saved
|
|
756
|
+
// value) is COLLAPSED. Persisted choice survives reloads. If the restored
|
|
757
|
+
// active tab lives inside the fold, force it open so the active rail button
|
|
758
|
+
// is visible on load.
|
|
759
|
+
let advancedFoldOpen = false;
|
|
760
|
+
try { advancedFoldOpen = localStorage.getItem(ADV_FOLD_KEY) === '1'; } catch { /* localStorage may be blocked in some embeds */ }
|
|
761
|
+
if (sections.some(s => s.id === activeTab && s.group === 'advanced')) advancedFoldOpen = true;
|
|
762
|
+
const foldStates = { advanced: advancedFoldOpen };
|
|
763
|
+
|
|
764
|
+
const railBtnHtml = (s, child) =>
|
|
765
|
+
'<button class="settings-rail-btn' + (s.featured ? ' featured' : '') + (child ? ' settings-rail-btn--child' : '') + (s.id === activeTab ? ' active' : '') + '" data-settings-tab="' + s.id + '" type="button">' +
|
|
746
766
|
'<span class="rail-label">' + escHtml(s.label) + '</span>' +
|
|
747
767
|
'<span class="match-count" style="display:none">0</span>' +
|
|
748
|
-
'</button>'
|
|
749
|
-
|
|
768
|
+
'</button>';
|
|
769
|
+
|
|
770
|
+
// Walk the sections in order. Leaves render directly; the first grouped
|
|
771
|
+
// section emits the whole fold (header + indented child list), and later
|
|
772
|
+
// members of the same group are skipped (they were rendered with the header).
|
|
773
|
+
const renderedGroups = new Set();
|
|
774
|
+
const railHtml = sections.map(function(s) {
|
|
775
|
+
if (!s.group) return railBtnHtml(s, false);
|
|
776
|
+
if (renderedGroups.has(s.group)) return '';
|
|
777
|
+
renderedGroups.add(s.group);
|
|
778
|
+
const members = sections.filter(x => x.group === s.group);
|
|
779
|
+
const open = !!foldStates[s.group];
|
|
780
|
+
const label = (RAIL_GROUPS[s.group] && RAIL_GROUPS[s.group].label) || s.group;
|
|
781
|
+
return '<button class="settings-rail-fold' + (open ? ' open' : '') + '" data-settings-fold="' + s.group + '" type="button" aria-expanded="' + (open ? 'true' : 'false') + '">' +
|
|
782
|
+
'<span class="fold-chevron" aria-hidden="true"></span>' +
|
|
783
|
+
'<span class="rail-label">' + escHtml(label) + '</span>' +
|
|
784
|
+
'<span class="match-count" style="display:none">0</span>' +
|
|
785
|
+
'</button>' +
|
|
786
|
+
'<div class="settings-rail-group' + (open ? ' open' : '') + '" data-settings-group="' + s.group + '" role="group" aria-label="' + escHtml(label) + '">' +
|
|
787
|
+
members.map(m => railBtnHtml(m, true)).join('') +
|
|
788
|
+
'</div>';
|
|
789
|
+
}).join('');
|
|
750
790
|
|
|
751
791
|
const panesHtml = sections.map(s =>
|
|
752
792
|
'<div class="settings-pane' + (s.id === activeTab ? ' active' : '') + '" data-settings-pane="' + s.id + '">' +
|
|
@@ -806,6 +846,20 @@ async function openSettings() {
|
|
|
806
846
|
});
|
|
807
847
|
});
|
|
808
848
|
|
|
849
|
+
// ── Wire up fold headers (expand/collapse the Advanced group) ───────────
|
|
850
|
+
// <button> headers are keyboard-operable natively (Enter/Space fire click).
|
|
851
|
+
// The user's choice is persisted so it survives reloads; the render-time
|
|
852
|
+
// default is collapsed unless the active tab lives inside the fold.
|
|
853
|
+
body.querySelectorAll('[data-settings-fold]').forEach(function(btn) {
|
|
854
|
+
btn.addEventListener('click', function() {
|
|
855
|
+
const gid = btn.getAttribute('data-settings-fold');
|
|
856
|
+
const open = _toggleSettingsFold(gid);
|
|
857
|
+
if (gid === 'advanced') {
|
|
858
|
+
try { localStorage.setItem(ADV_FOLD_KEY, open ? '1' : '0'); } catch { /* ignore */ }
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
|
|
809
863
|
// ── Cross-tab search box ───────────────────────────────────────────────
|
|
810
864
|
// Filters every [data-search] row across every pane. Empty query restores
|
|
811
865
|
// the active-tab view; non-empty query shows ALL panes with matches and
|
|
@@ -1257,6 +1311,32 @@ function _selectSettingsTab(id) {
|
|
|
1257
1311
|
body.querySelectorAll('[data-settings-pane]').forEach(function(pane) {
|
|
1258
1312
|
pane.classList.toggle('active', pane.getAttribute('data-settings-pane') === id);
|
|
1259
1313
|
});
|
|
1314
|
+
// If the selected tab lives inside a collapsed fold, expand the fold so its
|
|
1315
|
+
// rail button is visible. The pane renders regardless of fold state.
|
|
1316
|
+
const activeBtn = body.querySelector('[data-settings-tab="' + id + '"]');
|
|
1317
|
+
const group = activeBtn && activeBtn.closest('[data-settings-group]');
|
|
1318
|
+
if (group) {
|
|
1319
|
+
group.classList.add('open');
|
|
1320
|
+
const gid = group.getAttribute('data-settings-group');
|
|
1321
|
+
const fold = body.querySelector('[data-settings-fold="' + gid + '"]');
|
|
1322
|
+
if (fold) { fold.classList.add('open'); fold.setAttribute('aria-expanded', 'true'); }
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
// Expand/collapse a rail fold. Returns the resulting open state (or undefined
|
|
1327
|
+
// if the fold isn't mounted). `forceState` (boolean) sets an explicit state;
|
|
1328
|
+
// omitting it toggles. Keeps the header's aria-expanded + chevron in sync.
|
|
1329
|
+
function _toggleSettingsFold(groupId, forceState) {
|
|
1330
|
+
const body = document.getElementById('modal-body');
|
|
1331
|
+
if (!body) return undefined;
|
|
1332
|
+
const fold = body.querySelector('[data-settings-fold="' + groupId + '"]');
|
|
1333
|
+
const group = body.querySelector('[data-settings-group="' + groupId + '"]');
|
|
1334
|
+
if (!fold || !group) return undefined;
|
|
1335
|
+
const open = typeof forceState === 'boolean' ? forceState : !group.classList.contains('open');
|
|
1336
|
+
group.classList.toggle('open', open);
|
|
1337
|
+
fold.classList.toggle('open', open);
|
|
1338
|
+
fold.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
1339
|
+
return open;
|
|
1260
1340
|
}
|
|
1261
1341
|
|
|
1262
1342
|
function _applySettingsSearch(rawQuery) {
|
|
@@ -1281,6 +1361,25 @@ function _applySettingsSearch(rawQuery) {
|
|
|
1281
1361
|
const badge = btn.querySelector('.match-count');
|
|
1282
1362
|
if (badge) badge.style.display = 'none';
|
|
1283
1363
|
});
|
|
1364
|
+
// Restore each fold's resting state: persisted collapsed default, but kept
|
|
1365
|
+
// open if an active child tab lives inside it (so the visible pane's rail
|
|
1366
|
+
// button isn't hidden). Clears any search-driven fold badge/no-match.
|
|
1367
|
+
body.querySelectorAll('[data-settings-fold]').forEach(function(fold) {
|
|
1368
|
+
const gid = fold.getAttribute('data-settings-fold');
|
|
1369
|
+
const group = body.querySelector('[data-settings-group="' + gid + '"]');
|
|
1370
|
+
if (!group) return;
|
|
1371
|
+
fold.classList.remove('no-match');
|
|
1372
|
+
const foldBadge = fold.querySelector('.match-count');
|
|
1373
|
+
if (foldBadge) foldBadge.style.display = 'none';
|
|
1374
|
+
let open = false;
|
|
1375
|
+
if (gid === 'advanced') {
|
|
1376
|
+
try { open = localStorage.getItem('minions.settings.advancedFoldOpen') === '1'; } catch { /* ignore */ }
|
|
1377
|
+
}
|
|
1378
|
+
if (group.querySelector('[data-settings-tab].active')) open = true;
|
|
1379
|
+
group.classList.toggle('open', open);
|
|
1380
|
+
fold.classList.toggle('open', open);
|
|
1381
|
+
fold.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
1382
|
+
});
|
|
1284
1383
|
return;
|
|
1285
1384
|
}
|
|
1286
1385
|
|
|
@@ -1311,6 +1410,32 @@ function _applySettingsSearch(rawQuery) {
|
|
|
1311
1410
|
}
|
|
1312
1411
|
}
|
|
1313
1412
|
});
|
|
1413
|
+
|
|
1414
|
+
// Fold handling during search: expand any fold that has a matching child so
|
|
1415
|
+
// results aren't hidden behind a collapsed header, aggregate the child match
|
|
1416
|
+
// counts onto the fold header badge, and dim folds with zero matches. Runs
|
|
1417
|
+
// AFTER the per-pane loop so child rail badges are already computed.
|
|
1418
|
+
body.querySelectorAll('[data-settings-fold]').forEach(function(fold) {
|
|
1419
|
+
const gid = fold.getAttribute('data-settings-fold');
|
|
1420
|
+
const group = body.querySelector('[data-settings-group="' + gid + '"]');
|
|
1421
|
+
if (!group) return;
|
|
1422
|
+
let total = 0;
|
|
1423
|
+
let anyMatch = false;
|
|
1424
|
+
group.querySelectorAll('[data-settings-tab]').forEach(function(cb) {
|
|
1425
|
+
if (!cb.classList.contains('no-match')) anyMatch = true;
|
|
1426
|
+
const badge = cb.querySelector('.match-count');
|
|
1427
|
+
if (badge && badge.style.display !== 'none') total += Number(badge.textContent) || 0;
|
|
1428
|
+
});
|
|
1429
|
+
group.classList.toggle('open', anyMatch);
|
|
1430
|
+
fold.classList.toggle('open', anyMatch);
|
|
1431
|
+
fold.setAttribute('aria-expanded', anyMatch ? 'true' : 'false');
|
|
1432
|
+
fold.classList.toggle('no-match', !anyMatch);
|
|
1433
|
+
const foldBadge = fold.querySelector('.match-count');
|
|
1434
|
+
if (foldBadge) {
|
|
1435
|
+
foldBadge.textContent = String(total);
|
|
1436
|
+
foldBadge.style.display = total > 0 ? 'inline-block' : 'none';
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1314
1439
|
}
|
|
1315
1440
|
|
|
1316
1441
|
async function saveSettings() {
|
|
@@ -1682,7 +1807,7 @@ async function removeProject(name) {
|
|
|
1682
1807
|
}
|
|
1683
1808
|
}
|
|
1684
1809
|
|
|
1685
|
-
window.MinionsSettings = { openSettings, saveSettings, addProject, removeProject, resetSettingsToDefaults, _selectSettingsTab, _applySettingsSearch };
|
|
1810
|
+
window.MinionsSettings = { openSettings, saveSettings, addProject, removeProject, resetSettingsToDefaults, _selectSettingsTab, _applySettingsSearch, _toggleSettingsFold };
|
|
1686
1811
|
|
|
1687
1812
|
// ── Settings embed mode (Slim UX parity — W-mrkpke07000i8ed5) ───────────
|
|
1688
1813
|
// The Slim UX Settings modal embeds this LITERAL classic Settings surface in
|
package/dashboard/styles.css
CHANGED
|
@@ -1308,6 +1308,19 @@
|
|
|
1308
1308
|
.settings-rail-btn.active { color: var(--blue); border-left-color: var(--blue); background: var(--surface); font-weight: 600; }
|
|
1309
1309
|
.settings-rail-btn.no-match { opacity: 0.3; }
|
|
1310
1310
|
.settings-rail-btn .match-count { font-size: var(--text-xs); color: var(--blue); background: var(--bg); border-radius: var(--radius-xl); padding: 1px 6px; min-width: 18px; text-align: center; }
|
|
1311
|
+
/* W-mryz3r7h — collapsible rail fold ("Advanced"). The fold header is a real
|
|
1312
|
+
<button> (keyboard-operable); child rail buttons live in a container that
|
|
1313
|
+
is hidden unless the fold is .open. Children are indented under the header. */
|
|
1314
|
+
.settings-rail-fold { display: flex; align-items: center; gap: var(--space-2); width: 100%; padding: var(--space-3) var(--space-6); background: transparent; border: none; border-left: 3px solid transparent; color: var(--muted); font-size: var(--text-md); font-weight: 400; font-family: inherit; cursor: pointer; text-align: left; transition: all var(--transition-fast); }
|
|
1315
|
+
.settings-rail-fold:hover { color: var(--text); background: var(--surface); }
|
|
1316
|
+
.settings-rail-fold.no-match { opacity: 0.3; }
|
|
1317
|
+
.settings-rail-fold .rail-label { flex: 1; }
|
|
1318
|
+
.settings-rail-fold .match-count { font-size: var(--text-xs); color: var(--blue); background: var(--bg); border-radius: var(--radius-xl); padding: 1px 6px; min-width: 18px; text-align: center; }
|
|
1319
|
+
.settings-rail-fold .fold-chevron::before { content: '\25B8'; display: inline-block; font-size: var(--text-xs); color: var(--muted); transition: transform var(--transition-fast); }
|
|
1320
|
+
.settings-rail-fold.open .fold-chevron::before { transform: rotate(90deg); }
|
|
1321
|
+
.settings-rail-group { display: none; }
|
|
1322
|
+
.settings-rail-group.open { display: block; }
|
|
1323
|
+
.settings-rail-btn--child { padding-left: var(--space-9); }
|
|
1311
1324
|
.settings-content { flex: 1; overflow-y: auto; padding: var(--space-7) var(--space-8); min-width: 0; }
|
|
1312
1325
|
.settings-pane { display: none; }
|
|
1313
1326
|
.settings-pane.active { display: block; }
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Dev composite branch + dev-dashboard workflow
|
|
2
|
+
|
|
3
|
+
This documents the recurring "rebuild the composite branch from all my open PRs and
|
|
4
|
+
relaunch the dev dashboard" workflow. It exists because the flow is otherwise
|
|
5
|
+
reconstructed from `git reflog` every time. The [`rebuild-composite`](../.claude/skills/rebuild-composite/SKILL.md)
|
|
6
|
+
skill and [`scripts/rebuild-composite.ps1`](../scripts/rebuild-composite.ps1) automate it.
|
|
7
|
+
|
|
8
|
+
## What the composite branch is
|
|
9
|
+
|
|
10
|
+
`composite/calebt-prs` is a throwaway integration branch: `origin/main` plus every
|
|
11
|
+
one of an operator's currently-open PRs merged on top, so the whole in-flight set can
|
|
12
|
+
be exercised together in one running dev instance. It is **rebuilt from scratch each
|
|
13
|
+
time** (hard reset to `origin/main`, then re-merge each open PR) — never fast-forwarded
|
|
14
|
+
and never pushed. Because of the hard reset, **anything committed only to
|
|
15
|
+
`composite/calebt-prs` is discarded on the next rebuild**; durable changes (like this
|
|
16
|
+
doc, the skill, and the script) must land on `main` via a normal PR.
|
|
17
|
+
|
|
18
|
+
## The rebuild flow
|
|
19
|
+
|
|
20
|
+
1. Confirm you are on `composite/calebt-prs` and note any dirty tracked files.
|
|
21
|
+
2. Stash dirty tracked edits (see "Rogue dev edits" below) — do **not** discard them.
|
|
22
|
+
3. `git fetch origin --prune`.
|
|
23
|
+
4. Enumerate the operator's open PRs against `main`:
|
|
24
|
+
`gh pr list --repo <owner/repo> --state open --author <login> --base main --json number,headRefName`.
|
|
25
|
+
5. `git reset --hard origin/main`.
|
|
26
|
+
6. Merge each PR's head branch (`origin/<headRefName>`) in ascending PR-number order
|
|
27
|
+
with `git merge --no-edit`. Resolve conflicts, then `git commit --no-edit`.
|
|
28
|
+
7. `node --check` the touched `.js` files.
|
|
29
|
+
8. Relaunch the dev dashboard (see "Relaunching the dev dashboard").
|
|
30
|
+
|
|
31
|
+
## Recurring merge conflicts
|
|
32
|
+
|
|
33
|
+
Two conflicts have recurred on essentially every rebuild. Both are **clean additive**
|
|
34
|
+
conflicts (two branches append to the same spot); the resolution is always "keep both
|
|
35
|
+
sides". The canonical resolutions:
|
|
36
|
+
|
|
37
|
+
### `bin/minions.js` (from the `agent rekey` PR, e.g. #412)
|
|
38
|
+
|
|
39
|
+
`main` adds an `else if (cmd === 'state')` command block; the PR adds an
|
|
40
|
+
`else if (cmd === 'agent')` block at the same location. **Keep both** — the `state`
|
|
41
|
+
block first, then the `agent` block, then continue to the existing `doctor` block.
|
|
42
|
+
Simply delete the three conflict markers (`<<<<<<<`, `|||||||`/`=======`, `>>>>>>>`);
|
|
43
|
+
no lines between the sides need to be dropped.
|
|
44
|
+
|
|
45
|
+
### `dashboard/slim/styles.css` (from the slim-ux Engine-embed PR, e.g. #896)
|
|
46
|
+
|
|
47
|
+
`main` has the `#slim-settings-modal` block; the PR adds `.slim-engine-embed,` to the
|
|
48
|
+
front of the `.slim-*-embed` selector group. **Keep both** — the settings-modal block,
|
|
49
|
+
then `.slim-engine-embed,`, then the existing `.slim-work-embed, …` group.
|
|
50
|
+
|
|
51
|
+
> **Strongly recommended: `git rerere`.** These resolutions are byte-identical every
|
|
52
|
+
> rebuild. Enabling `git config rerere.enabled true` records each resolution once and
|
|
53
|
+
> replays it automatically on every subsequent rebuild, so the conflicts resolve
|
|
54
|
+
> themselves. The rerere cache lives in `.git/` and therefore survives the hard reset.
|
|
55
|
+
|
|
56
|
+
Stop and resolve manually only when a **new** conflict (any other path) appears.
|
|
57
|
+
|
|
58
|
+
## Relaunching the dev dashboard
|
|
59
|
+
|
|
60
|
+
`--dev` mode runs the current checkout **as** the runtime root (`MINIONS_HOME = checkout`)
|
|
61
|
+
with the dashboard on port **7332** (default) so it coexists with a global install on
|
|
62
|
+
7331. See `bin/minions.js` (`--dev` / `--dev-port`).
|
|
63
|
+
|
|
64
|
+
Rebuild is not a compile step — the dashboard SPA is reassembled at startup — so
|
|
65
|
+
"rebuild + relaunch" is just a dev restart:
|
|
66
|
+
|
|
67
|
+
```powershell
|
|
68
|
+
node bin/minions.js --dev restart # or: npm run dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Restart-under-load gotchas
|
|
72
|
+
|
|
73
|
+
When the box is saturated (e.g. the dev fleet is running the full test suite), the
|
|
74
|
+
`--dev restart` health verifier can **fast-fail**: the freshly spawned engine can't
|
|
75
|
+
write its pid file inside the verifier's short window, so it is reported
|
|
76
|
+
`state=stopped, pid=none`, the partial stack is torn down, and `engine/stop-intent.json`
|
|
77
|
+
is set — which then blocks the next start, creating a loop. A **stray orphaned
|
|
78
|
+
`dashboard.js`** from an earlier attempt independently trips the verifier's
|
|
79
|
+
duplicate-daemon check.
|
|
80
|
+
|
|
81
|
+
Reliable recovery:
|
|
82
|
+
|
|
83
|
+
1. Stop any stray `engine.js` / `dashboard.js` / `supervisor.js` daemons (leave
|
|
84
|
+
`dispatch-*` agent processes and their worktree children alone — those are detached
|
|
85
|
+
agents that reattach on the next start).
|
|
86
|
+
2. Delete `engine/stop-intent.json`.
|
|
87
|
+
3. Confirm nothing is listening on the dev port.
|
|
88
|
+
4. Start with `node bin/minions.js --dev start` — from a clean state this is more
|
|
89
|
+
reliable than `restart` under load.
|
|
90
|
+
5. Verify: `GET http://localhost:7332/api/status` returns HTTP 200.
|
|
91
|
+
|
|
92
|
+
The engine itself boots fine under load (a foreground `node engine.js` stays healthy);
|
|
93
|
+
the fragility is only in the restart verifier's fast-fail path.
|
|
94
|
+
|
|
95
|
+
## Rogue dev edits
|
|
96
|
+
|
|
97
|
+
Because `--dev` uses the checkout as the runtime root, the dev fleet's own agents
|
|
98
|
+
sometimes edit **tracked** files in place (e.g. `dashboard/js/settings.js`, docs, even
|
|
99
|
+
reverting `README.md`). These show up as dirty tracked files before a rebuild. This is
|
|
100
|
+
expected, not corruption — stash them (with a dated message) rather than discarding, so
|
|
101
|
+
nothing is lost, then rebuild.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2415",
|
|
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"
|