@yemi33/minions 0.1.2306 → 0.1.2308
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/bin/minions.js +1 -0
- package/dashboard/js/settings.js +31 -1
- package/dashboard/slim/body.html +6 -22
- package/dashboard/slim/js/modals-tiles.js +32 -22
- package/dashboard/slim/js/plans.js +24 -516
- package/dashboard/slim/styles.css +10 -7
- package/dashboard.js +63 -8
- package/docs/completion-reports.md +2 -2
- package/docs/harness-transparency.md +18 -0
- package/docs/live-checkout-mode.md +53 -6
- package/engine/ado-comment.js +5 -2
- package/engine/cc-worker-pool.js +16 -4
- package/engine/cli.js +58 -3
- package/engine/comment-format.js +82 -2
- package/engine/discover-project-skills.js +4 -0
- package/engine/dispatch.js +36 -1
- package/engine/gh-comment.js +8 -3
- package/engine/lifecycle.js +92 -25
- package/engine/live-checkout.js +207 -0
- package/engine/pre-dispatch-eval.js +54 -5
- package/engine/shared.js +90 -1
- package/engine/supervisor.js +13 -0
- package/engine/watchdog.js +10 -0
- package/engine.js +208 -3
- package/package.json +1 -1
- package/playbooks/review.md +2 -0
- package/playbooks/shared-rules.md +9 -0
package/bin/minions.js
CHANGED
|
@@ -1580,6 +1580,7 @@ ${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
|
|
|
1580
1580
|
isPortListening,
|
|
1581
1581
|
confirmPortUp: tcpPortAccepts,
|
|
1582
1582
|
isStopIntentSet: shared.isStopIntentSet || (() => false),
|
|
1583
|
+
recordRespawn: shared.recordEngineRespawn,
|
|
1583
1584
|
}).then(result => {
|
|
1584
1585
|
// ALWAYS exit 0 — the scheduler must never see a failure for the
|
|
1585
1586
|
// recovery we're trying to make boring. The `result` is logged by tick
|
package/dashboard/js/settings.js
CHANGED
|
@@ -289,6 +289,23 @@ async function openSettings() {
|
|
|
289
289
|
'⚠ Live mode: dispatches run directly in this repo\'s checkout. Only one mutating dispatch runs at a time. Dirty working trees block dispatch — commit or stash before running.' +
|
|
290
290
|
'</div>' +
|
|
291
291
|
'</div>';
|
|
292
|
+
// W-mqtvnnj1000357fa — per-project live-checkout auto-stash override.
|
|
293
|
+
// Tri-state: "" (use fleet default → engine.liveCheckoutAutoStash), "on"
|
|
294
|
+
// (force stash), "off" (force fail-on-dirty). Only an explicit on/off
|
|
295
|
+
// overrides the fleet setting; the default option leaves p.liveCheckoutAutoStash
|
|
296
|
+
// unset so resolveLiveCheckoutAutoStash falls back to engine.
|
|
297
|
+
var autoStashRaw = (p.liveCheckoutAutoStash === true) ? 'on' : (p.liveCheckoutAutoStash === false) ? 'off' : '';
|
|
298
|
+
var autoStashSearch = 'live checkout auto-stash dirty tree stash dispatch';
|
|
299
|
+
var autoStashBlock =
|
|
300
|
+
'<div data-search="' + escHtml(autoStashSearch) + '" style="margin-bottom:6px">' +
|
|
301
|
+
'<label style="font-size:var(--text-sm);color:var(--muted);display:block;margin-bottom:2px">Live-checkout auto-stash</label>' +
|
|
302
|
+
'<select id="set-liveCheckoutAutoStash-' + escHtml(p.name) + '" style="width:100%;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:var(--text-md)">' +
|
|
303
|
+
'<option value=""' + (autoStashRaw === '' ? ' selected' : '') + '>Use fleet default</option>' +
|
|
304
|
+
'<option value="on"' + (autoStashRaw === 'on' ? ' selected' : '') + '>On — auto-stash dirty tree</option>' +
|
|
305
|
+
'<option value="off"' + (autoStashRaw === 'off' ? ' selected' : '') + '>Off — fail on dirty tree</option>' +
|
|
306
|
+
'</select>' +
|
|
307
|
+
'<div style="font-size:var(--text-xs);color:var(--muted);margin-top:2px;line-height:1.4">Live mode only. On = `git stash push --include-untracked` a dirty tree before dispatch instead of failing; the engine never pops the stash (run `git stash pop` manually). Overrides the fleet-wide setting under Worktrees.</div>' +
|
|
308
|
+
'</div>';
|
|
292
309
|
return '<div data-settings-project="' + escHtml(p.name) + '" data-search="project ' + escHtml(p.name.toLowerCase()) + '" style="border:1px solid var(--border);border-radius:6px;padding:10px 12px;margin-bottom:12px">' +
|
|
293
310
|
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">' +
|
|
294
311
|
'<div style="font-size:var(--text-md);font-weight:600">' + escHtml(p.name) + '</div>' +
|
|
@@ -297,6 +314,7 @@ async function openSettings() {
|
|
|
297
314
|
pathRow +
|
|
298
315
|
branchGrid +
|
|
299
316
|
worktreeModeBlock +
|
|
317
|
+
autoStashBlock +
|
|
300
318
|
driftNote +
|
|
301
319
|
'<div style="display:flex;flex-direction:column;gap:6px;margin-top:8px">' +
|
|
302
320
|
settingsToggle('Discover from PRs', 'set-ws-prs-' + p.name, p.workSources.pullRequests.enabled, 'Discovery gate: scan repo for open PRs and surface them as review tasks. Independent of ADO/GitHub polling — does not affect already-tracked PRs.') +
|
|
@@ -1050,6 +1068,7 @@ async function saveSettings() {
|
|
|
1050
1068
|
autoCompletePrs: document.getElementById('set-autoCompletePrs').checked,
|
|
1051
1069
|
ccUseWorkerPool: !!document.getElementById('set-ccUseWorkerPool')?.checked,
|
|
1052
1070
|
autoReapOrphanWorktreeHolders: !!document.getElementById('set-autoReapOrphanWorktreeHolders')?.checked,
|
|
1071
|
+
liveCheckoutAutoStash: !!document.getElementById('set-liveCheckoutAutoStash')?.checked,
|
|
1053
1072
|
liveCheckoutAutoReset: !!document.getElementById('set-liveCheckoutAutoReset')?.checked,
|
|
1054
1073
|
orphanHolderScanTimeoutMs: document.getElementById('set-orphanHolderScanTimeoutMs')?.value,
|
|
1055
1074
|
statusProbeKillTimeoutMs: document.getElementById('set-statusProbeKillTimeoutMs')?.value,
|
|
@@ -1095,8 +1114,11 @@ async function saveSettings() {
|
|
|
1095
1114
|
// Settings UI presents the idle timeout in minutes for readability; the
|
|
1096
1115
|
// engine stores ms (ENGINE_DEFAULTS.ccWorkerIdleTimeoutMs). Convert here;
|
|
1097
1116
|
// the server clamps to [60000, 28800000] ms. undefined when blank so the
|
|
1098
|
-
// server keeps the prior value rather than zeroing it.
|
|
1117
|
+
// server keeps the prior value rather than zeroing it. W-mr1qbj90000d30bd:
|
|
1118
|
+
// the "Never idle-reap CC workers" toggle wins and submits the explicit
|
|
1119
|
+
// `0` sentinel (bypasses the clamp server-side), ignoring the minutes field.
|
|
1099
1120
|
ccWorkerIdleTimeoutMs: (function () {
|
|
1121
|
+
if (document.getElementById('set-ccNeverIdleReap')?.checked) return 0;
|
|
1100
1122
|
var m = Number(document.getElementById('set-ccWorkerIdleTimeoutMs')?.value);
|
|
1101
1123
|
return (Number.isFinite(m) && m > 0) ? Math.round(m * 60000) : undefined;
|
|
1102
1124
|
})(),
|
|
@@ -1155,10 +1177,18 @@ async function saveSettings() {
|
|
|
1155
1177
|
// values.
|
|
1156
1178
|
const wtModeInput = document.getElementById('set-checkoutMode-' + p.name);
|
|
1157
1179
|
const wtModeValue = (wtModeInput && wtModeInput.value === 'live') ? 'live' : 'worktree';
|
|
1180
|
+
// W-mqtvnnj1000357fa — per-project live-checkout auto-stash override.
|
|
1181
|
+
// '' → null (clear override, fall back to engine fleet setting); 'on' →
|
|
1182
|
+
// true; 'off' → false. The server deletes the field on null and validates
|
|
1183
|
+
// booleans.
|
|
1184
|
+
const autoStashInput = document.getElementById('set-liveCheckoutAutoStash-' + p.name);
|
|
1185
|
+
const autoStashRaw = autoStashInput ? autoStashInput.value : '';
|
|
1186
|
+
const autoStashValue = autoStashRaw === 'on' ? true : autoStashRaw === 'off' ? false : null;
|
|
1158
1187
|
return {
|
|
1159
1188
|
name: p.name,
|
|
1160
1189
|
mainBranch: mainBranchValue || null,
|
|
1161
1190
|
checkoutMode: wtModeValue,
|
|
1191
|
+
liveCheckoutAutoStash: autoStashValue,
|
|
1162
1192
|
workSources: {
|
|
1163
1193
|
pullRequests: { enabled: document.getElementById('set-ws-prs-' + p.name)?.checked ?? true },
|
|
1164
1194
|
workItems: { enabled: document.getElementById('set-ws-wi-' + p.name)?.checked ?? true }
|
package/dashboard/slim/body.html
CHANGED
|
@@ -226,28 +226,12 @@
|
|
|
226
226
|
</div>
|
|
227
227
|
</div>
|
|
228
228
|
|
|
229
|
-
<!-- Plans + PRD control panel —
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
archive,delete,unarchive}).
|
|
236
|
-
PRD — the .json entries from /api/plans plus /api/prd and the
|
|
237
|
-
read-only subset of /api/prd-items. -->
|
|
238
|
-
<div class="modal-bg" id="slim-plans-modal">
|
|
239
|
-
<div class="modal slim-plans-modal-inner">
|
|
240
|
-
<div class="modal-header">
|
|
241
|
-
<h3>Plans</h3>
|
|
242
|
-
<div class="kn-tabs" id="slim-plans-tabs" role="tablist">
|
|
243
|
-
<button class="kn-tab active" id="slim-plans-tab-plans" data-plans-tab="plans" type="button" role="tab" aria-selected="true" aria-controls="slim-plans-body">Plans</button>
|
|
244
|
-
<button class="kn-tab" id="slim-plans-tab-prd" data-plans-tab="prd" type="button" role="tab" aria-selected="false" aria-controls="slim-plans-body">PRD</button>
|
|
245
|
-
</div>
|
|
246
|
-
<button id="slim-plans-close" class="icon-btn" title="Close" style="margin-left:auto">×</button>
|
|
247
|
-
</div>
|
|
248
|
-
<div class="modal-body" id="slim-plans-body" role="tabpanel" tabindex="0" aria-labelledby="slim-plans-tab-plans"></div>
|
|
249
|
-
</div>
|
|
250
|
-
</div>
|
|
229
|
+
<!-- Plans + PRD control panel — the "Plans" cockpit tile opens the LITERAL
|
|
230
|
+
classic /plans screen in a chrome-off iframe (/plans?embed=1), routed through
|
|
231
|
+
the standard TILE_VIEWS path in modals-tiles.js#renderPlansBody. There is no
|
|
232
|
+
slim-specific Plans/PRD flyout markup anymore (W-mr28ko750010199f / "reuse,
|
|
233
|
+
don't fork"): the classic /plans screen is the single source of truth for the
|
|
234
|
+
Plans + PRD tabs and every lifecycle action. -->
|
|
251
235
|
|
|
252
236
|
<!-- Pin editor — create (Pin Content action / "+ Pin") or edit an existing
|
|
253
237
|
note. Submits to POST /api/pinned (create) or /api/pinned/update (edit). -->
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
bindModalClose('slim-agent-modal', 'slim-agent-close', _stopAgentDetailRuntime);
|
|
18
18
|
bindModalClose('slim-tools-modal', 'slim-tools-close');
|
|
19
19
|
bindModalClose('slim-tile-modal', 'slim-tile-close');
|
|
20
|
-
bindModalClose('slim-plans-modal', 'slim-plans-close');
|
|
21
20
|
|
|
22
21
|
function tileEmpty(body, text) {
|
|
23
22
|
var d = document.createElement('div');
|
|
@@ -142,6 +141,25 @@
|
|
|
142
141
|
body.appendChild(frame);
|
|
143
142
|
}
|
|
144
143
|
|
|
144
|
+
// Plans tile body — reuses the LITERAL classic dashboard /plans screen instead
|
|
145
|
+
// of a slim-specific tabbed Plans/PRD flyout, so there is one Plans/PRD UI, not
|
|
146
|
+
// two (W-mr28ko750010199f / "reuse, don't fork" — mirrors renderQueuedWorkBody
|
|
147
|
+
// for the Work tile). Slim and classic are two separate IIFE bundles with no
|
|
148
|
+
// shared scope, so we embed the real /plans screen in an iframe with the
|
|
149
|
+
// chrome-off ?embed=1 mode. The iframed page IS the classic screen — the full
|
|
150
|
+
// Plans + PRD tabbed UI with every lifecycle action (approve/execute/reject/
|
|
151
|
+
// pause/regenerate/unarchive/archive/delete), backed by the same /api/plans +
|
|
152
|
+
// /api/prd data + endpoints — with zero duplicated rendering logic. Classic is
|
|
153
|
+
// reachable at /plans even with slim-ux ON (only / is taken over). Built with
|
|
154
|
+
// createElement (no innerHTML) to satisfy the dashboard no-unsanitized lint gate.
|
|
155
|
+
function renderPlansBody(body) {
|
|
156
|
+
var frame = document.createElement('iframe');
|
|
157
|
+
frame.className = 'slim-plans-embed';
|
|
158
|
+
frame.src = '/plans?embed=1';
|
|
159
|
+
frame.title = 'Plans';
|
|
160
|
+
body.appendChild(frame);
|
|
161
|
+
}
|
|
162
|
+
|
|
145
163
|
function renderPrTileBody(body, data) {
|
|
146
164
|
// Emoji glyphs make build/review status scannable at a glance; the
|
|
147
165
|
// accompanying label keeps the row readable when the glyph alone is
|
|
@@ -264,6 +282,7 @@
|
|
|
264
282
|
engine: { title: 'Engine', render: renderEngineTileBody },
|
|
265
283
|
dispatches: { title: 'Active dispatches', render: function(body, data) { renderDispatchTileBody(body, data, true); } },
|
|
266
284
|
queued: { title: 'Queued work', render: function(body) { renderQueuedWorkBody(body); } },
|
|
285
|
+
plans: { title: 'Plans', render: function(body) { renderPlansBody(body); } },
|
|
267
286
|
prs: { title: 'Pull requests', render: renderPrTileBody },
|
|
268
287
|
watches: { title: 'Watches', render: renderWatchTileBody },
|
|
269
288
|
};
|
|
@@ -274,9 +293,6 @@
|
|
|
274
293
|
// The Knowledge tile opens the unified Knowledge control panel (Pinned
|
|
275
294
|
// Context + Notes + KB tabs) rather than the read-only tile detail view.
|
|
276
295
|
if (key === 'knowledge') { openKnowledgeModal(); return; }
|
|
277
|
-
// The Plans tile opens the tabbed Plans/PRD control panel (rendered by
|
|
278
|
-
// dashboard/slim/js/plans.js) rather than the read-only tile detail view.
|
|
279
|
-
if (key === 'plans') { openPlansModal(); return; }
|
|
280
296
|
var view = TILE_VIEWS[key];
|
|
281
297
|
if (!view) return;
|
|
282
298
|
var modal = document.getElementById('slim-tile-modal');
|
|
@@ -288,9 +304,11 @@
|
|
|
288
304
|
// The "+ Link PR" header chip only applies to the PR list.
|
|
289
305
|
var headerChip = document.getElementById('slim-tile-modal-linkpr');
|
|
290
306
|
if (headerChip) headerChip.style.display = (key === 'prs') ? '' : 'none';
|
|
291
|
-
// The queued
|
|
292
|
-
// the modal + drop the body padding so the embedded
|
|
307
|
+
// The queued + plans tiles embed a full classic screen (/work, /plans) in an
|
|
308
|
+
// iframe — widen the modal + drop the body padding so the embedded screen
|
|
309
|
+
// gets real estate. Both reuse the shared wide-modal treatment.
|
|
293
310
|
modal.classList.toggle('tile-modal--work', key === 'queued');
|
|
311
|
+
modal.classList.toggle('tile-modal--plans', key === 'plans');
|
|
294
312
|
view.render(body, lastStatusData || {});
|
|
295
313
|
modal.classList.add('open');
|
|
296
314
|
}
|
|
@@ -316,22 +334,14 @@
|
|
|
316
334
|
}
|
|
317
335
|
})();
|
|
318
336
|
|
|
319
|
-
// ── Plans
|
|
320
|
-
// The Plans tile opens
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
if (tabBar) {
|
|
328
|
-
tabBar.addEventListener('click', function(ev) {
|
|
329
|
-
var btn = ev.target && ev.target.closest ? ev.target.closest('.kn-tab') : null;
|
|
330
|
-
if (!btn) return;
|
|
331
|
-
var tab = btn.getAttribute('data-plans-tab');
|
|
332
|
-
if (tab && typeof setPlansTab === 'function') setPlansTab(tab);
|
|
333
|
-
});
|
|
334
|
-
}
|
|
337
|
+
// ── Plans tile wiring ──────────────────────────────────────────────
|
|
338
|
+
// The Plans tile opens the classic /plans screen in an iframe (routed through
|
|
339
|
+
// the standard TILE_VIEWS path in openTileModal above) — there is no
|
|
340
|
+
// slim-specific Plans/PRD flyout to wire. The cockpit tile's at-a-glance count
|
|
341
|
+
// is still slim-owned (the plan/PRD count is not part of the /api/status poll),
|
|
342
|
+
// so it is fetched lazily from /api/plans shortly after first paint, mirroring
|
|
343
|
+
// knowledge.js#loadKnowledgeCounts. See dashboard/slim/js/plans.js.
|
|
344
|
+
(function bindPlansTileCount() {
|
|
335
345
|
if (typeof loadPlansCounts === 'function') setTimeout(loadPlansCounts, 1500);
|
|
336
346
|
})();
|
|
337
347
|
|