@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.
- package/dashboard/js/confirm-dialog.js +124 -0
- package/dashboard/js/render-pinned.js +13 -2
- package/dashboard/layout.html +16 -0
- package/dashboard/styles.css +12 -0
- package/dashboard-build.js +1 -1
- package/dashboard.js +1 -1
- package/docs/completion-reports.md +5 -5
- package/docs/index.html +1 -1
- package/docs/onboarding.md.rej +10 -0
- package/docs/pr-comment-followup.md +4 -4
- package/docs/rfc-completion-json.md +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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(); }
|
package/dashboard/layout.html
CHANGED
|
@@ -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>
|
package/dashboard/styles.css
CHANGED
|
@@ -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; }
|
package/dashboard-build.js
CHANGED
|
@@ -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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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 — built by <a href="https://github.com/
|
|
275
|
+
<p>Minions — built by <a href="https://github.com/opg-microsoft/minions">opg-microsoft</a> — <a href="https://www.npmjs.com/package/@yemi33/minions">npm</a> — <a href="https://github.com/opg-microsoft/minions">Repository</a> — MIT License — 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/
|
|
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/
|
|
58
|
-
"parent_pr_id": "github:
|
|
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/
|
|
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/
|
|
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.
|
|
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"
|