@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 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
- - **Claude Code CLI** — install with `npm install -g @anthropic-ai/claude-code`
15
- - **Anthropic API key** or Claude Max subscription (agents spawn Claude Code sessions)
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.
@@ -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 'claude'.
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 || 'claude');
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
- window.MinionsPrs = { prRow, prTableHtml, renderPrs, prPrev, prNext, openAllPrs, openModal, openAddPrModal, unlinkPr };
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 };
@@ -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 || 'claude';
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 'claude' as a fallback option even if /api/runtimes is empty;
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) : ['claude'];
412
- const currentDefault = engineCfg.defaultCli || 'claude';
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 || 'claude';
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] || {};
@@ -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;