@yemi33/minions 0.1.2140 → 0.1.2142

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.
@@ -0,0 +1,124 @@
1
+ // confirm-dialog.js — Promise-based custom confirm dialog primitive.
2
+ //
3
+ // Replaces native window.confirm() for actions where the dashboard wants
4
+ // a styled, accessible modal. The HTML scaffold lives in layout.html
5
+ // (#confirm-dialog) and the styles live in styles.css (.confirm-card,
6
+ // .confirm-actions). Both the trigger and the dialog buttons are wired
7
+ // to the same Promise so callers can `await confirmDialog({...})`.
8
+ //
9
+ // Behavior contract (matches WI W-mq5dk1lq):
10
+ // - Escape closes (resolves false)
11
+ // - Backdrop click closes (resolves false)
12
+ // - Enter activates the focused button (default focus is the confirm
13
+ // button, so Enter == confirm in the common case)
14
+ // - Tab / Shift+Tab cycle between Cancel and Confirm (focus trap)
15
+ // - Focus returns to the element that opened the dialog on close
16
+ // - role="dialog", aria-modal="true", aria-labelledby points at the title
17
+ //
18
+ // API:
19
+ // confirmDialog({title, message, confirmLabel, cancelLabel, danger})
20
+ // -> Promise<boolean> (true = confirmed, false = cancelled)
21
+
22
+ (function() {
23
+ let _activeResolve = null;
24
+ let _previousFocus = null;
25
+ let _keyHandler = null;
26
+
27
+ function getEl() { return document.getElementById('confirm-dialog'); }
28
+
29
+ function getFocusable(scope) {
30
+ return Array.from(scope.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'))
31
+ .filter(el => !el.disabled && el.offsetParent !== null);
32
+ }
33
+
34
+ function closeDialog(result) {
35
+ const el = getEl();
36
+ if (!el) return;
37
+ el.classList.remove('open');
38
+ if (_keyHandler) {
39
+ document.removeEventListener('keydown', _keyHandler, true);
40
+ _keyHandler = null;
41
+ }
42
+ const resolver = _activeResolve;
43
+ _activeResolve = null;
44
+ // Restore focus to the trigger element (button/link/etc) on close.
45
+ const prev = _previousFocus;
46
+ _previousFocus = null;
47
+ if (prev && typeof prev.focus === 'function') {
48
+ try { prev.focus(); } catch { /* ignore — element may be gone */ }
49
+ }
50
+ if (resolver) resolver(!!result);
51
+ }
52
+
53
+ function confirmDialog(opts) {
54
+ opts = opts || {};
55
+ const el = getEl();
56
+ if (!el) {
57
+ // Defensive: if the HTML scaffold is missing, fall back to native
58
+ // confirm so the calling action still works (e.g. in a test harness
59
+ // that loads the JS without the layout).
60
+ return Promise.resolve(window.confirm(opts.message || opts.title || 'Confirm?'));
61
+ }
62
+ // If a prior dialog is somehow still open, cancel it before reusing
63
+ // the shared scaffold.
64
+ if (_activeResolve) closeDialog(false);
65
+
66
+ const titleEl = el.querySelector('#confirm-dialog-title');
67
+ const msgEl = el.querySelector('#confirm-dialog-message');
68
+ const confirmBtn = el.querySelector('#confirm-dialog-confirm');
69
+ const cancelBtn = el.querySelector('#confirm-dialog-cancel');
70
+
71
+ titleEl.textContent = opts.title || 'Confirm';
72
+ msgEl.textContent = opts.message || '';
73
+ confirmBtn.textContent = opts.confirmLabel || 'OK';
74
+ cancelBtn.textContent = opts.cancelLabel || 'Cancel';
75
+ // Style the confirm action: red border/text for destructive intent,
76
+ // solid blue for the default reversible/action case. Both reuse the
77
+ // existing .btn primitive in styles.css so the dialog inherits the
78
+ // app's button look.
79
+ confirmBtn.className = 'btn ' + (opts.danger ? 'btn-danger' : 'btn-primary');
80
+ cancelBtn.className = 'btn';
81
+
82
+ _previousFocus = document.activeElement;
83
+
84
+ return new Promise((resolve) => {
85
+ _activeResolve = resolve;
86
+
87
+ el.classList.add('open');
88
+ // Default-focus the confirm button so Enter activates it natively.
89
+ // (Spec: "Enter submits (confirm)".) Defer to next tick so the
90
+ // browser sees the dialog as visible before focus is moved.
91
+ setTimeout(() => { try { confirmBtn.focus(); } catch { /* ignore */ } }, 0);
92
+
93
+ _keyHandler = function(ev) {
94
+ if (!el.classList.contains('open')) return;
95
+ if (ev.key === 'Escape') {
96
+ ev.preventDefault();
97
+ closeDialog(false);
98
+ return;
99
+ }
100
+ if (ev.key === 'Tab') {
101
+ // Focus trap: keep tab cycling between the two buttons.
102
+ const focusable = getFocusable(el);
103
+ if (focusable.length === 0) return;
104
+ const first = focusable[0];
105
+ const last = focusable[focusable.length - 1];
106
+ if (ev.shiftKey && document.activeElement === first) {
107
+ ev.preventDefault();
108
+ last.focus();
109
+ } else if (!ev.shiftKey && document.activeElement === last) {
110
+ ev.preventDefault();
111
+ first.focus();
112
+ }
113
+ }
114
+ // Enter / Space are handled natively by whichever button has focus.
115
+ };
116
+ document.addEventListener('keydown', _keyHandler, true);
117
+ });
118
+ }
119
+
120
+ window.confirmDialog = confirmDialog;
121
+ // Exposed so the inline onclick handlers on the dialog backdrop /
122
+ // Cancel / Confirm buttons can resolve the promise.
123
+ window._confirmDialogResolve = closeDialog;
124
+ })();
@@ -77,10 +77,21 @@ async function submitPinnedNote(e) {
77
77
  }
78
78
 
79
79
  async function removePinnedNote(title) {
80
- if (!confirm('Unpin "' + title + '"?')) return;
80
+ // Capture the trigger before awaiting so confirmDialog can restore focus
81
+ // to the actual Unpin button (window.event is cleared by the time the
82
+ // dialog resolves).
83
+ const btn = (window.event)?.target;
84
+ const ok = await confirmDialog({
85
+ title: 'Unpin context?',
86
+ message: 'This will remove "' + title + '" from your pinned context. You can re-pin it anytime.',
87
+ confirmLabel: 'Unpin',
88
+ cancelLabel: 'Cancel',
89
+ danger: false,
90
+ });
91
+ if (!ok) return;
81
92
  showToast('cmd-toast', 'Note unpinned', true);
82
93
  markDeleted('pin:' + title);
83
- const btn = (window.event)?.target; if (btn) { const card = btn.closest('.pinned-card') || btn.parentElement?.parentElement; if (card) card.remove(); }
94
+ if (btn) { const card = btn.closest('.pinned-card') || btn.parentElement?.parentElement; if (card) card.remove(); }
84
95
  try {
85
96
  const res = await fetch('/api/pinned/remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title }) });
86
97
  if (!res.ok) { clearDeleted('pin:' + title); showToast('cmd-toast', 'Unpin failed', false); refresh(); }
@@ -159,6 +159,22 @@
159
159
  <!-- Floating "Ask about selection" button -->
160
160
  <div class="ask-selection-btn" id="ask-selection-btn" onclick="modalAskAboutSelection()">Ask about this</div>
161
161
 
162
+ <!-- Custom confirm dialog — replaces native window.confirm() for actions
163
+ that want the dashboard's styled, accessible modal (W-mq5dk1lq).
164
+ The contents are filled in by confirmDialog() in confirm-dialog.js. -->
165
+ <div class="modal-bg confirm-dialog-bg" id="confirm-dialog" role="dialog" aria-modal="true" aria-labelledby="confirm-dialog-title" onclick="if(event.target===this)_confirmDialogResolve(false)">
166
+ <div class="modal confirm-card">
167
+ <div class="modal-header">
168
+ <h3 id="confirm-dialog-title">Confirm</h3>
169
+ </div>
170
+ <div class="modal-body confirm-message" id="confirm-dialog-message"></div>
171
+ <div class="confirm-actions">
172
+ <button type="button" class="btn" id="confirm-dialog-cancel" onclick="_confirmDialogResolve(false)">Cancel</button>
173
+ <button type="button" class="btn btn-primary" id="confirm-dialog-confirm" onclick="_confirmDialogResolve(true)">OK</button>
174
+ </div>
175
+ </div>
176
+ </div>
177
+
162
178
  <script>/* __JS__ */</script>
163
179
  </body>
164
180
  </html>
@@ -745,6 +745,18 @@
745
745
  .modal-copy.copied { color: var(--green); border-color: var(--green); }
746
746
  .modal-close { background: none; border: none; color: var(--muted); font-size: var(--text-2xl); cursor: pointer; padding: var(--space-2) var(--space-4); }
747
747
  .modal-close:hover { color: var(--text); }
748
+
749
+ /* Custom confirm dialog (W-mq5dk1lq) — promise-based replacement for
750
+ native window.confirm(). Scaffold in layout.html (#confirm-dialog),
751
+ behavior in dashboard/js/confirm-dialog.js. Reuses .modal-bg / .modal
752
+ for backdrop + card chrome; the rules below size the card down to a
753
+ compact "are you sure?" footprint and style the action row. */
754
+ .confirm-dialog-bg { z-index: 500; }
755
+ .modal.confirm-card { width: auto; max-width: 480px; min-width: 320px; }
756
+ .confirm-message { white-space: normal; font-family: 'Segoe UI', system-ui, sans-serif; font-size: var(--text-md); color: var(--text); line-height: 1.5; }
757
+ .confirm-actions { display: flex; justify-content: flex-end; gap: var(--space-4); padding: var(--space-5) var(--space-8) var(--space-7); }
758
+ .confirm-actions .btn { min-width: 80px; }
759
+ .confirm-actions .btn:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }
748
760
  .notif-badge { position: absolute; top: -5px; right: -5px; pointer-events: none; }
749
761
  .notif-badge.done { width: 8px; height: 8px; background: var(--red); border-radius: 50%; animation: notifPulse 2s infinite; }
750
762
  .notif-badge.processing { display: flex; gap: 2px; align-items: center; }
@@ -34,7 +34,7 @@ function buildDashboardHtml() {
34
34
  'render-prs', 'render-plans', 'render-inbox', 'render-kb', 'render-skills',
35
35
  'render-other', 'render-managed', 'render-schedules', 'render-watches', 'render-pipelines', 'render-meetings', 'render-pinned',
36
36
  'command-parser', 'command-input', 'command-center', 'command-history',
37
- 'modal', 'modal-qa', 'settings', 'qa', 'fre', 'refresh'
37
+ 'confirm-dialog', 'modal', 'modal-qa', 'settings', 'qa', 'fre', 'refresh'
38
38
  ];
39
39
  let jsHtml = '';
40
40
  for (const f of jsFiles) {
package/dashboard.js CHANGED
@@ -1098,7 +1098,7 @@ function buildDashboardHtml() {
1098
1098
  'render-prs', 'render-plans', 'render-inbox', 'render-kb', 'render-skills',
1099
1099
  'render-other', 'render-managed', 'render-schedules', 'render-watches', 'render-pipelines', 'render-meetings', 'render-pinned',
1100
1100
  'command-parser', 'command-input', 'command-center', 'command-history',
1101
- 'modal', 'modal-qa', 'settings', 'qa', 'fre', 'refresh'
1101
+ 'confirm-dialog', 'modal', 'modal-qa', 'settings', 'qa', 'fre', 'refresh'
1102
1102
  ];
1103
1103
  let jsHtml = '';
1104
1104
  for (const f of jsFiles) {
@@ -143,7 +143,7 @@ optional `followups` array on its completion report:
143
143
  {
144
144
  "status": "success",
145
145
  "summary": "Fixed reviewer's blocking finding on PR #2400; dispatched W-mp9abcdef as out-of-scope follow-up.",
146
- "pr": "https://github.com/yemi33/minions/pull/2400",
146
+ "pr": "https://github.com/opg-microsoft/minions/pull/2400",
147
147
  "followups": [
148
148
  {
149
149
  "wi_id": "W-mp9abcdef",
@@ -207,12 +207,12 @@ If the JSON report exists and is well-formed, the engine ignores the fenced bloc
207
207
  "status": "success",
208
208
  "summary": "Added stale-HEAD guard to push step in spawn-agent.js; verified with new unit test test/unit/spawn-agent.test.js (4632 passed, 0 failed)",
209
209
  "verdict": null,
210
- "pr": "https://github.com/yemi33/minions/pull/2360",
210
+ "pr": "https://github.com/opg-microsoft/minions/pull/2360",
211
211
  "failure_class": "N/A",
212
212
  "retryable": false,
213
213
  "needs_rerun": false,
214
214
  "artifacts": [
215
- {"type": "pr", "path": "https://github.com/yemi33/minions/pull/2360", "title": "PR-2360"},
215
+ {"type": "pr", "path": "https://github.com/opg-microsoft/minions/pull/2360", "title": "PR-2360"},
216
216
  {"type": "file", "path": "engine/spawn-agent.js", "title": "Stale-HEAD guard"},
217
217
  {"type": "file", "path": "test/unit/spawn-agent.test.js", "title": "New unit test"}
218
218
  ]
@@ -226,12 +226,12 @@ If the JSON report exists and is well-formed, the engine ignores the fenced bloc
226
226
  "status": "success",
227
227
  "summary": "Verified completeRun sweep is inside mutateJsonFileLocked callback and pre-existing terminal stages are preserved.",
228
228
  "verdict": "approved",
229
- "pr": "https://github.com/yemi33/minions/pull/2313",
229
+ "pr": "https://github.com/opg-microsoft/minions/pull/2313",
230
230
  "failure_class": "N/A",
231
231
  "retryable": false,
232
232
  "needs_rerun": false,
233
233
  "artifacts": [
234
- {"type": "pr", "path": "https://github.com/yemi33/minions/pull/2313", "title": "PR-2313"}
234
+ {"type": "pr", "path": "https://github.com/opg-microsoft/minions/pull/2313", "title": "PR-2313"}
235
235
  ]
236
236
  }
237
237
  ```
package/docs/index.html CHANGED
@@ -272,7 +272,7 @@ minions update</pre>
272
272
 
273
273
  </div>
274
274
  <footer>
275
- <p>Minions &mdash; built by <a href="https://github.com/yemi33">yemi33</a> &mdash; <a href="https://www.npmjs.com/package/@yemi33/minions">npm</a> &mdash; <a href="https://yemi33.github.io/minions/">Public site</a> &mdash; MIT License &mdash; Claude Code or GitHub Copilot CLI runtime</p>
275
+ <p>Minions &mdash; built by <a href="https://github.com/opg-microsoft/minions">opg-microsoft</a> &mdash; <a href="https://www.npmjs.com/package/@yemi33/minions">npm</a> &mdash; <a href="https://github.com/opg-microsoft/minions">Repository</a> &mdash; MIT License &mdash; Claude Code or GitHub Copilot CLI runtime</p>
276
276
  </footer>
277
277
 
278
278
  </body>
@@ -0,0 +1,10 @@
1
+ diff a/docs/onboarding.md b/docs/onboarding.md (rejected hunks)
2
+ @@ -13,7 +13,7 @@ A fast, hands-on walkthrough for new contributors. Follow the eight steps below
3
+ You only need to clone if you intend to modify Minions itself. End users normally `npm install -g @yemi33/minions` instead, but a checkout is the right starting point for contributors.
4
+
5
+ ```bash
6
+ -git clone https://github.com/yemi33/minions.git ~/minions-dev
7
+ +git clone https://github.com/opg-microsoft/minions.git ~/minions-dev
8
+ cd ~/minions-dev
9
+ npm install # installs dev tooling (Playwright); engine itself has zero deps
10
+ ```
@@ -50,12 +50,12 @@ playbook contract. The poller is unchanged; the carve-out is purely additive.
50
50
  "project": "minions",
51
51
  "description": "Reviewer asked to track separately from PR #2400…",
52
52
  "references": [
53
- {"url": "https://github.com/yemi33/minions/pull/2400", "label": "Originated from PR #2400 comment"}
53
+ {"url": "https://github.com/opg-microsoft/minions/pull/2400", "label": "Originated from PR #2400 comment"}
54
54
  ],
55
55
  "meta": {
56
56
  "pr_followup": {
57
- "parent_pr_url": "https://github.com/yemi33/minions/pull/2400",
58
- "parent_pr_id": "github:yemi33/minions#2400",
57
+ "parent_pr_url": "https://github.com/opg-microsoft/minions/pull/2400",
58
+ "parent_pr_id": "github:opg-microsoft/minions#2400",
59
59
  "parent_comment_id": "4567890",
60
60
  "parent_comment_author": "alice"
61
61
  }
@@ -91,7 +91,7 @@ playbook contract. The poller is unchanged; the carve-out is purely additive.
91
91
  {
92
92
  "status": "success",
93
93
  "summary": "Fixed reviewer's blocking finding; dispatched W-mp9abcdef as out-of-scope follow-up.",
94
- "pr": "https://github.com/yemi33/minions/pull/2400",
94
+ "pr": "https://github.com/opg-microsoft/minions/pull/2400",
95
95
  "followups": [
96
96
  {
97
97
  "wi_id": "W-mp9abcdef",
@@ -108,7 +108,7 @@ The agent must not write the file in pieces. Empty, truncated, or malformed JSON
108
108
  "prs": [
109
109
  {
110
110
  "number": 1234,
111
- "url": "https://github.com/yemi33/minions/pull/1234",
111
+ "url": "https://github.com/opg-microsoft/minions/pull/1234",
112
112
  "branch": "feat/P-7a8b9c1d-rfc-completion-json",
113
113
  "title": "feat: RFC for completion.json control-plane",
114
114
  "host": "github", // "github" | "ado"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2140",
3
+ "version": "0.1.2142",
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"