@yemi33/minions 0.1.2045 → 0.1.2047
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/README.md +2 -2
- package/dashboard/js/fre.js +3 -2
- package/dashboard/js/render-prs.js +82 -2
- package/dashboard/js/settings.js +5 -5
- package/dashboard/styles.css +11 -0
- package/dashboard.js +376 -135
- package/docs/copilot-cli-schema.md +2 -1
- package/docs/runtime-adapters.md +9 -4
- package/engine/cc-worker-pool.js +87 -11
- package/engine/llm.js +148 -2
- package/engine/preflight.js +5 -5
- package/engine/queries.js +75 -35
- package/engine/runtimes/claude.js +41 -0
- package/engine/runtimes/copilot.js +97 -3
- package/engine/shared.js +4 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@ Inspired by and initially scaffolded from [Brady Gaster's Squad](https://bradyga
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
13
13
|
- **Node.js** 18+ (LTS recommended)
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
14
|
+
- **A supported runtime CLI** — Minions defaults to GitHub Copilot CLI (`npm install -g @github/copilot`). Claude Code CLI (`npm install -g @anthropic-ai/claude-code`) is also supported; switch with `minions config set-cli claude` or per-agent `cli` overrides.
|
|
15
|
+
- **Auth for your runtime** — GitHub Copilot subscription (Copilot CLI handles its own auth) or an Anthropic API key / Claude Max subscription
|
|
16
16
|
- **Git** — agents create worktrees for all code changes
|
|
17
17
|
|
|
18
18
|
> **Note:** you do **not** need to configure your CLI for "autopilot" / "bypass permissions" / "dangerous mode". Minions passes the right bypass flag per spawn (`--dangerously-skip-permissions` for Claude; `--autopilot --allow-all --no-ask-user` for Copilot), independent of your global CLI config. Run `minions doctor` to verify your installed CLI accepts those flags.
|
package/dashboard/js/fre.js
CHANGED
|
@@ -80,9 +80,10 @@ function renderFre(statusOrProjects) {
|
|
|
80
80
|
|
|
81
81
|
// Resolve the currently-configured runtime CLI for the explainer copy.
|
|
82
82
|
// /api/status surfaces this as autoMode.defaultCli (resolveAgentCli(null, engine)).
|
|
83
|
-
// Fall back to autoMode.ccCli (also defaultCli-derived when ccCli unset) then '
|
|
83
|
+
// Fall back to autoMode.ccCli (also defaultCli-derived when ccCli unset) then 'copilot'
|
|
84
|
+
// (matches ENGINE_DEFAULTS.defaultCli — W-mpmwxkk40007c995).
|
|
84
85
|
const auto = (status && status.autoMode) || {};
|
|
85
|
-
const runtimeCli = String(auto.defaultCli || auto.ccCli || '
|
|
86
|
+
const runtimeCli = String(auto.defaultCli || auto.ccCli || 'copilot');
|
|
86
87
|
|
|
87
88
|
const cardStyle = [
|
|
88
89
|
'margin:12px 24px',
|
|
@@ -22,6 +22,16 @@ function _countPrFollowups(pr) {
|
|
|
22
22
|
return n;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// W-mpmwxkzm0009ba0b — Parse a canonical PR id like `github:octo/repo#123`
|
|
26
|
+
// or `ado:org/proj/repo#456` into the {host, slug, number} triple used by
|
|
27
|
+
// POST /api/pull-requests/observe. Returns null when the id doesn't match.
|
|
28
|
+
function _parseCanonicalPrId(id) {
|
|
29
|
+
if (!id || typeof id !== 'string') return null;
|
|
30
|
+
var m = id.match(/^(github|ado):(.+)#(\d+)$/);
|
|
31
|
+
if (!m) return null;
|
|
32
|
+
return { host: m[1], slug: m[2], number: parseInt(m[3], 10) };
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
function prRow(pr) {
|
|
26
36
|
// Minions review (agent) state — separate from ADO human review
|
|
27
37
|
const sq = pr.minionsReview || {};
|
|
@@ -61,6 +71,29 @@ function prRow(pr) {
|
|
|
61
71
|
? '<span class="pr-agent" title="' + escapeHtml(pr.reviewedBy.join(', ')) + '">' + escapeHtml(pr.reviewedBy.join(', ')) + '</span>'
|
|
62
72
|
: '<span style="color:var(--muted);font-size:11px">—</span>';
|
|
63
73
|
const createdLabel = (pr.created || '—').slice(0, 16).replace('T', ' ');
|
|
74
|
+
// Per-row auto-observe toggle (W-mpmwxkzm0009ba0b). _contextOnly === true
|
|
75
|
+
// means the engine polls status/comments but does NOT dispatch review/fix
|
|
76
|
+
// agents against this PR. Default (undefined / false) = observed.
|
|
77
|
+
var observe = pr._contextOnly !== true;
|
|
78
|
+
var observeClass = observe ? 'pr-observe-on' : 'pr-observe-off';
|
|
79
|
+
var observeLabel = observe ? 'observed' : 'context';
|
|
80
|
+
var observeTitle = observe
|
|
81
|
+
? 'Auto-observe ON — engine reviews this PR and dispatches fix agents on review/build feedback. Click to switch to context-only.'
|
|
82
|
+
: 'Context only — engine polls status and comments but will not dispatch review/fix agents. Click to enable auto-observe.';
|
|
83
|
+
// pr.id is canonical: "<host>:<slug>#<number>" (engine/shared.js getCanonicalPrId).
|
|
84
|
+
// Parse here so the toggle handler can post {host, slug, number} without
|
|
85
|
+
// shipping additional fields per row.
|
|
86
|
+
var observeBtn = '';
|
|
87
|
+
var idMatch = typeof pr.id === 'string' ? pr.id.match(/^([^:]+):(.+)#(\d+)$/) : null;
|
|
88
|
+
if (idMatch) {
|
|
89
|
+
observeBtn = '<button class="pr-observe-toggle ' + observeClass + '" '
|
|
90
|
+
+ 'data-pr-host="' + escapeHtml(idMatch[1]) + '" '
|
|
91
|
+
+ 'data-pr-slug="' + escapeHtml(idMatch[2]) + '" '
|
|
92
|
+
+ 'data-pr-number="' + escapeHtml(idMatch[3]) + '" '
|
|
93
|
+
+ 'data-pr-observe="' + (observe ? '1' : '0') + '" '
|
|
94
|
+
+ 'title="' + escapeHtml(observeTitle) + '" '
|
|
95
|
+
+ 'onclick="event.stopPropagation();togglePrObserve(this)">' + observeLabel + '</button> ';
|
|
96
|
+
}
|
|
64
97
|
// Title attrs live on the inner element (link/span/badge) so hovering the
|
|
65
98
|
// ellipsis-truncated content reveals the full text. Cell tags stay bare so
|
|
66
99
|
// the header-to-cell count assertion in test/unit.test.js continues to
|
|
@@ -74,6 +107,7 @@ function prRow(pr) {
|
|
|
74
107
|
'<td>' + reviewerCell + '</td>' +
|
|
75
108
|
'<td><span class="pr-badge ' + buildClass + '" title="' + escapeHtml(buildTitle || buildLabel) + '">' + escapeHtml(buildLabel) + '</span></td>' +
|
|
76
109
|
'<td><span class="pr-badge ' + statusClass + '" title="' + escapeHtml(statusLabel) + '">' + escapeHtml(statusLabel) + '</span></td>' +
|
|
110
|
+
'<td>' + (observeBtn || '<span style="color:var(--muted);font-size:11px">—</span>') + '</td>' +
|
|
77
111
|
'<td><span class="pr-date" title="' + escapeHtml(createdLabel) + '">' + escapeHtml(createdLabel) + '</span></td>' +
|
|
78
112
|
'<td><button class="pr-pager-btn" style="font-size:9px;padding:1px 5px;color:var(--red);border-color:var(--red)" data-pr-id="' + escapeHtml(String(prId)) + '" onclick="event.stopPropagation();unlinkPr(this.dataset.prId)" title="Remove from tracking">x</button></td>' +
|
|
79
113
|
'</tr>';
|
|
@@ -93,6 +127,7 @@ const PRS_COLGROUP =
|
|
|
93
127
|
'<col style="width:140px">' + // Signed Off By
|
|
94
128
|
'<col style="width:130px">' + // Build
|
|
95
129
|
'<col style="width:110px">' + // Status
|
|
130
|
+
'<col style="width:100px">' + // Observe
|
|
96
131
|
'<col style="width:130px">' + // Created
|
|
97
132
|
'<col style="width:50px">' + // Actions
|
|
98
133
|
'</colgroup>';
|
|
@@ -101,7 +136,7 @@ function prTableHtml(rows) {
|
|
|
101
136
|
return '<div class="pr-table-wrap pr-table-wrap--prs"><table class="pr-table pr-table--prs">' +
|
|
102
137
|
PRS_COLGROUP +
|
|
103
138
|
'<thead><tr>' +
|
|
104
|
-
'<th>PR</th><th>Title</th><th>Agent</th><th>Branch</th><th>Review</th><th>Signed Off By</th><th>Build</th><th>Status</th><th>Created</th><th></th>' +
|
|
139
|
+
'<th>PR</th><th>Title</th><th>Agent</th><th>Branch</th><th>Review</th><th>Signed Off By</th><th>Build</th><th>Status</th><th>Observe</th><th>Created</th><th></th>' +
|
|
105
140
|
'</tr></thead><tbody>' + rows + '</tbody></table></div>';
|
|
106
141
|
}
|
|
107
142
|
|
|
@@ -249,4 +284,49 @@ async function unlinkPr(id) {
|
|
|
249
284
|
} catch (e) { clearDeleted('pr:' + id); showToast('pr-toast', 'Error: ' + e.message, false); refresh(); }
|
|
250
285
|
}
|
|
251
286
|
|
|
252
|
-
|
|
287
|
+
// Per-row auto-observe toggle (W-mpmwxkzm0009ba0b). Optimistic UI: flip
|
|
288
|
+
// the button immediately, only revert on API error. The engine consumes
|
|
289
|
+
// `_contextOnly` to gate review/fix dispatch (engine/shared.js
|
|
290
|
+
// isAutoManagedPrRecord + discoverFromPrs).
|
|
291
|
+
async function togglePrObserve(btn) {
|
|
292
|
+
if (!btn || btn.disabled) return;
|
|
293
|
+
const host = btn.dataset.prHost;
|
|
294
|
+
const slug = btn.dataset.prSlug;
|
|
295
|
+
const number = parseInt(btn.dataset.prNumber, 10);
|
|
296
|
+
if (!host || !slug || !Number.isFinite(number)) return;
|
|
297
|
+
const wasObserve = btn.dataset.prObserve === '1';
|
|
298
|
+
const next = !wasObserve;
|
|
299
|
+
// Optimistic flip
|
|
300
|
+
btn.dataset.prObserve = next ? '1' : '0';
|
|
301
|
+
btn.classList.toggle('pr-observe-on', next);
|
|
302
|
+
btn.classList.toggle('pr-observe-off', !next);
|
|
303
|
+
btn.textContent = next ? 'observed' : 'context';
|
|
304
|
+
btn.disabled = true;
|
|
305
|
+
try {
|
|
306
|
+
const res = await fetch('/api/pull-requests/observe', {
|
|
307
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
308
|
+
body: JSON.stringify({ host, slug, number, observe: next })
|
|
309
|
+
});
|
|
310
|
+
if (!res.ok) {
|
|
311
|
+
const d = await res.json().catch(() => ({}));
|
|
312
|
+
// Revert
|
|
313
|
+
btn.dataset.prObserve = wasObserve ? '1' : '0';
|
|
314
|
+
btn.classList.toggle('pr-observe-on', wasObserve);
|
|
315
|
+
btn.classList.toggle('pr-observe-off', !wasObserve);
|
|
316
|
+
btn.textContent = wasObserve ? 'observed' : 'context';
|
|
317
|
+
showToast('pr-toast', 'Failed: ' + (d.error || 'unknown'), false);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
showToast('pr-toast', next ? 'Auto-observe enabled' : 'Switched to context only', true);
|
|
321
|
+
} catch (e) {
|
|
322
|
+
btn.dataset.prObserve = wasObserve ? '1' : '0';
|
|
323
|
+
btn.classList.toggle('pr-observe-on', wasObserve);
|
|
324
|
+
btn.classList.toggle('pr-observe-off', !wasObserve);
|
|
325
|
+
btn.textContent = wasObserve ? 'observed' : 'context';
|
|
326
|
+
showToast('pr-toast', 'Error: ' + e.message, false);
|
|
327
|
+
} finally {
|
|
328
|
+
btn.disabled = false;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
window.MinionsPrs = { prRow, prTableHtml, renderPrs, prPrev, prNext, openAllPrs, openModal, openAddPrModal, unlinkPr, togglePrObserve };
|
package/dashboard/js/settings.js
CHANGED
|
@@ -49,7 +49,7 @@ async function openSettings() {
|
|
|
49
49
|
// Per-agent override placeholders surface the inherited fleet defaults as
|
|
50
50
|
// muted text — operators see exactly what each agent will resolve to without
|
|
51
51
|
// chasing config files. Empty input clears the override → re-inherit fleet.
|
|
52
|
-
const fleetCliLabel = e.defaultCli || '
|
|
52
|
+
const fleetCliLabel = e.defaultCli || 'copilot';
|
|
53
53
|
const fleetModelLabel = e.defaultModel ? String(e.defaultModel) : 'CLI default';
|
|
54
54
|
const agentRows = Object.entries(agents).map(function([id, a]) {
|
|
55
55
|
return '<tr>' +
|
|
@@ -406,10 +406,10 @@ async function initRuntimeFleetUI(engineCfg, agentsCfg) {
|
|
|
406
406
|
runtimes = Array.isArray(d.runtimes) ? d.runtimes : [];
|
|
407
407
|
} catch { /* ignore — we'll surface a free-text-only path below */ }
|
|
408
408
|
|
|
409
|
-
// Always include '
|
|
409
|
+
// Always include 'copilot' as a fallback option even if /api/runtimes is empty;
|
|
410
410
|
// legacy installs without the registry endpoint should still see something pickable.
|
|
411
|
-
const names = runtimes.length ? runtimes.map(rt => rt.name) : ['
|
|
412
|
-
const currentDefault = engineCfg.defaultCli || '
|
|
411
|
+
const names = runtimes.length ? runtimes.map(rt => rt.name) : ['copilot'];
|
|
412
|
+
const currentDefault = engineCfg.defaultCli || 'copilot';
|
|
413
413
|
const currentCc = engineCfg.ccCli || '';
|
|
414
414
|
cliSelect.innerHTML = names.map(n =>
|
|
415
415
|
'<option value="' + escHtml(n) + '"' + (n === currentDefault ? ' selected' : '') + '>' + escHtml(n) + '</option>'
|
|
@@ -440,7 +440,7 @@ async function initRuntimeFleetUI(engineCfg, agentsCfg) {
|
|
|
440
440
|
// this the input was free-text and a user could (and did) save an agent
|
|
441
441
|
// with cli=claude + model=<some gpt> — invalid combination that crashed
|
|
442
442
|
// dispatch. Refreshing on CLI change clears stale model values.
|
|
443
|
-
const fleetDefaultCli = engineCfg.defaultCli || '
|
|
443
|
+
const fleetDefaultCli = engineCfg.defaultCli || 'copilot';
|
|
444
444
|
for (const cell of cliCells) {
|
|
445
445
|
const agentId = cell.getAttribute('data-runtime-cli');
|
|
446
446
|
const agent = (agentsCfg || {})[agentId] || {};
|
package/dashboard/styles.css
CHANGED
|
@@ -322,6 +322,17 @@
|
|
|
322
322
|
.pr-page-info { font-size: var(--text-base); color: var(--muted); }
|
|
323
323
|
.pr-date { font-size: var(--text-base); color: var(--muted); }
|
|
324
324
|
|
|
325
|
+
.pr-observe-toggle {
|
|
326
|
+
font-size: 9px; padding: 1px 6px; border-radius: var(--radius-sm); cursor: pointer;
|
|
327
|
+
text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600;
|
|
328
|
+
background: var(--surface2); transition: all var(--transition-base);
|
|
329
|
+
}
|
|
330
|
+
.pr-observe-toggle:disabled { opacity: 0.6; cursor: wait; }
|
|
331
|
+
.pr-observe-toggle.pr-observe-on { color: var(--green); border: 1px solid var(--green); background: rgba(63,185,80,0.12); }
|
|
332
|
+
.pr-observe-toggle.pr-observe-on:hover:not(:disabled) { background: rgba(63,185,80,0.22); }
|
|
333
|
+
.pr-observe-toggle.pr-observe-off { color: var(--muted); border: 1px dashed var(--border); }
|
|
334
|
+
.pr-observe-toggle.pr-observe-off:hover:not(:disabled) { color: var(--text); border-color: var(--text); }
|
|
335
|
+
|
|
325
336
|
.archive-btn {
|
|
326
337
|
background: var(--surface2); border: 1px solid var(--border); color: var(--muted);
|
|
327
338
|
font-size: var(--text-base); padding: var(--space-2) var(--space-5); border-radius: var(--radius-sm); cursor: pointer; transition: all var(--transition-base); margin-left: auto;
|