@yemi33/minions 0.1.2176 → 0.1.2177
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/utils.js +8 -5
- package/dashboard/slim/body.html +12 -9
- package/dashboard/slim/js/command-send.js +5 -1
- package/dashboard/slim/js/modals-tiles.js +89 -7
- package/dashboard/slim/js/projects.js +36 -28
- package/dashboard/slim/js/status.js +52 -4
- package/dashboard/slim/styles.css +165 -20
- package/dashboard.js +8 -1
- package/package.json +1 -1
package/dashboard/js/utils.js
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
// Signal the engine to tick immediately (pick up new work without waiting 60s)
|
|
4
4
|
function wakeEngine() { fetch('/api/engine/wakeup', { method: 'POST' }).catch(() => {}); }
|
|
5
5
|
|
|
6
|
-
// "Try new Slim UX" — flips the slim-ux feature flag ON then
|
|
7
|
-
// root route serves the slim cockpit (the takeover is checked at request
|
|
8
|
-
// so no engine restart is needed).
|
|
9
|
-
// the
|
|
6
|
+
// "Try new Slim UX" — flips the slim-ux feature flag ON then navigates to the
|
|
7
|
+
// root route so it serves the slim cockpit (the takeover is checked at request
|
|
8
|
+
// time, so no engine restart is needed). We navigate to '/' rather than
|
|
9
|
+
// location.reload() because the slim takeover is gated on pathname === '/';
|
|
10
|
+
// reloading from a non-home classic page (/work, /prs, …) preserves the path
|
|
11
|
+
// and silently re-serves the classic dashboard. The first-visit welcome popup
|
|
12
|
+
// is gated on the browser side in dashboard/slim/js/settings.js, not here.
|
|
10
13
|
async function trySlimUx(btn) {
|
|
11
14
|
if (btn) { btn.disabled = true; btn.textContent = 'Switching…'; }
|
|
12
15
|
try {
|
|
@@ -16,7 +19,7 @@ async function trySlimUx(btn) {
|
|
|
16
19
|
body: JSON.stringify({ id: 'slim-ux', enabled: true }),
|
|
17
20
|
});
|
|
18
21
|
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
19
|
-
location.
|
|
22
|
+
location.href = '/';
|
|
20
23
|
} catch (e) {
|
|
21
24
|
if (btn) { btn.disabled = false; btn.textContent = '✨ Try new Slim UX'; }
|
|
22
25
|
alert('Could not switch to Slim UX: ' + (e && e.message ? e.message : 'unknown error'));
|
package/dashboard/slim/body.html
CHANGED
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
<div class="panel panel-actions">
|
|
22
22
|
<div class="panel-header">
|
|
23
23
|
Actions
|
|
24
|
-
<span class="panel-
|
|
24
|
+
<span class="panel-head-meta">
|
|
25
|
+
<span class="cc-model-badge" id="cc-model-badge" hidden></span>
|
|
26
|
+
<span class="panel-sub">Command Center & quick triggers</span>
|
|
27
|
+
</span>
|
|
25
28
|
</div>
|
|
26
29
|
<div class="actions-chat">
|
|
27
30
|
<div class="chat-tabs" id="chat-tabs"></div>
|
|
@@ -39,7 +42,7 @@
|
|
|
39
42
|
rows="2"
|
|
40
43
|
placeholder="Ask anything or give a command... (Enter to send, Shift+Enter for newline)"
|
|
41
44
|
></textarea>
|
|
42
|
-
<button id="chat-stop" class="chat-stop" type="button" title="Stop the in-flight response"
|
|
45
|
+
<button id="chat-stop" class="chat-stop" type="button" title="Stop the in-flight response" aria-label="Stop agent"><svg class="chat-stop-icon" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"></circle><rect x="8" y="8" width="8" height="8" rx="1" fill="currentColor"></rect></svg></button>
|
|
43
46
|
<button id="chat-send" class="chat-send" type="button">Send</button>
|
|
44
47
|
</div>
|
|
45
48
|
</div>
|
|
@@ -77,23 +80,23 @@
|
|
|
77
80
|
count now lives in the Team cards above, so that tile is removed. -->
|
|
78
81
|
<div class="cockpit-section">
|
|
79
82
|
<div class="cockpit-grid" id="cockpit-grid">
|
|
80
|
-
<div class="cockpit-tile" data-tile="engine">
|
|
83
|
+
<div class="cockpit-tile cockpit-tile--full" data-tile="engine">
|
|
81
84
|
<div class="cockpit-label"><span class="cockpit-dot"></span> Engine</div>
|
|
82
85
|
<div class="cockpit-value dim">—</div>
|
|
83
86
|
<div class="cockpit-detail">checking…</div>
|
|
84
87
|
</div>
|
|
85
|
-
<div class="cockpit-tile" data-tile="dispatches">
|
|
86
|
-
<div class="cockpit-label"><span class="cockpit-dot"></span> Active dispatches</div>
|
|
87
|
-
<div class="cockpit-value dim">0</div>
|
|
88
|
-
<div class="cockpit-detail">none in flight</div>
|
|
89
|
-
</div>
|
|
90
88
|
<div class="cockpit-tile" data-tile="queued">
|
|
91
89
|
<div class="cockpit-label"><span class="cockpit-dot"></span> Queued work</div>
|
|
92
90
|
<div class="cockpit-value dim">0</div>
|
|
93
91
|
<div class="cockpit-detail">queue empty</div>
|
|
94
92
|
</div>
|
|
93
|
+
<div class="cockpit-tile" data-tile="dispatches">
|
|
94
|
+
<div class="cockpit-label"><span class="cockpit-dot"></span> Active dispatches</div>
|
|
95
|
+
<div class="cockpit-value dim">0</div>
|
|
96
|
+
<div class="cockpit-detail">no agents working</div>
|
|
97
|
+
</div>
|
|
95
98
|
<div class="cockpit-tile" data-tile="prs">
|
|
96
|
-
<button id="slim-tile-linkpr-chip" class="linkpr-chip on-tile" type="button" title="Link a pull request"
|
|
99
|
+
<button id="slim-tile-linkpr-chip" class="linkpr-chip on-tile" type="button" title="Link a pull request" aria-label="Link a pull request">+</button>
|
|
97
100
|
<div class="cockpit-label"><span class="cockpit-dot"></span> Active PRs</div>
|
|
98
101
|
<div class="cockpit-value dim">0</div>
|
|
99
102
|
<div class="cockpit-detail">no open PRs</div>
|
|
@@ -102,7 +102,11 @@
|
|
|
102
102
|
var res = await fetch('/api/command-center/stream', {
|
|
103
103
|
method: 'POST',
|
|
104
104
|
headers: { 'Content-Type': 'application/json' },
|
|
105
|
-
|
|
105
|
+
// currentProject is the slim project picker's explicit selection (null
|
|
106
|
+
// when the indicator reads "Select project"). noProjectSelected lets the
|
|
107
|
+
// server inject a "no project — ask the user" CC preamble instead of
|
|
108
|
+
// silently defaulting; only the slim composer sends it (W-mqayzsj3).
|
|
109
|
+
body: JSON.stringify({ message: text, tabId: tabId, sessionId: sessionId, currentProject: currentProject || undefined, noProjectSelected: currentProject ? undefined : true }),
|
|
106
110
|
signal: signal,
|
|
107
111
|
});
|
|
108
112
|
if (!res.ok) {
|
|
@@ -23,7 +23,15 @@
|
|
|
23
23
|
body.appendChild(d);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
// One list row for the tile detail modal.
|
|
26
|
+
// One list row for the tile detail modal.
|
|
27
|
+
// opts: { title, meta, metaNodes, subtle, subtleBeforeMeta, chip:{text,cls}, href }.
|
|
28
|
+
// meta — plain-text meta line.
|
|
29
|
+
// metaNodes — array of Nodes for the meta line, interleaved with
|
|
30
|
+
// ' · ' separators; takes precedence over `meta`.
|
|
31
|
+
// subtle — optional muted second line (e.g. org/project); skipped
|
|
32
|
+
// when empty.
|
|
33
|
+
// subtleBeforeMeta — when true, subtle renders directly under the title
|
|
34
|
+
// (above the meta row); default is below the meta row.
|
|
27
35
|
// Built as an <a> when href is present so PR rows open the PR in a new tab.
|
|
28
36
|
function tileCard(opts) {
|
|
29
37
|
var card = document.createElement(opts.href ? 'a' : 'div');
|
|
@@ -43,15 +51,45 @@
|
|
|
43
51
|
top.appendChild(chip);
|
|
44
52
|
}
|
|
45
53
|
card.appendChild(top);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
function appendSubtle() {
|
|
55
|
+
if (!opts.subtle) return;
|
|
56
|
+
var subtle = document.createElement('div');
|
|
57
|
+
subtle.className = 'tile-item-subtle';
|
|
58
|
+
subtle.textContent = opts.subtle;
|
|
59
|
+
card.appendChild(subtle);
|
|
51
60
|
}
|
|
61
|
+
function appendMeta() {
|
|
62
|
+
if (opts.metaNodes && opts.metaNodes.length) {
|
|
63
|
+
var metaRow = document.createElement('div');
|
|
64
|
+
metaRow.className = 'tile-item-meta';
|
|
65
|
+
opts.metaNodes.forEach(function(node, i) {
|
|
66
|
+
if (i > 0) metaRow.appendChild(document.createTextNode(' · '));
|
|
67
|
+
metaRow.appendChild(node);
|
|
68
|
+
});
|
|
69
|
+
card.appendChild(metaRow);
|
|
70
|
+
} else if (opts.meta) {
|
|
71
|
+
var meta = document.createElement('div');
|
|
72
|
+
meta.className = 'tile-item-meta';
|
|
73
|
+
meta.textContent = opts.meta;
|
|
74
|
+
card.appendChild(meta);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (opts.subtleBeforeMeta) { appendSubtle(); appendMeta(); }
|
|
78
|
+
else { appendMeta(); appendSubtle(); }
|
|
52
79
|
return card;
|
|
53
80
|
}
|
|
54
81
|
|
|
82
|
+
// Parse the org/project slug from a canonical PR id, dropping the host prefix
|
|
83
|
+
// and the trailing "#<num>". E.g. "ado:office/iss/constellation#5315603" →
|
|
84
|
+
// "office/iss/constellation", "github:yemi33/minions#2911" → "yemi33/minions".
|
|
85
|
+
function parsePrSlug(id) {
|
|
86
|
+
if (!id || typeof id !== 'string') return '';
|
|
87
|
+
var hash = id.indexOf('#');
|
|
88
|
+
var head = hash >= 0 ? id.slice(0, hash) : id;
|
|
89
|
+
var colon = head.indexOf(':');
|
|
90
|
+
return colon >= 0 ? head.slice(colon + 1) : head;
|
|
91
|
+
}
|
|
92
|
+
|
|
55
93
|
// Per-tile body renderers for the detail modal. Each reads the latest status
|
|
56
94
|
// snapshot and fills the modal body; openTileModal looks them up by key.
|
|
57
95
|
function renderEngineTileBody(body, data) {
|
|
@@ -83,17 +121,61 @@
|
|
|
83
121
|
}
|
|
84
122
|
|
|
85
123
|
function renderPrTileBody(body, data) {
|
|
124
|
+
// Emoji glyphs make build/review status scannable at a glance; the
|
|
125
|
+
// accompanying label keeps the row readable when the glyph alone is
|
|
126
|
+
// ambiguous (✅ vs ✅, ❌ vs ❌, ⏳ vs 💬). The raw status string also
|
|
127
|
+
// stays in the title attribute for hover discoverability.
|
|
128
|
+
// Keyed by normalized (lowercased) status; missing/unknown → ➖.
|
|
129
|
+
var BUILD_STATUS = {
|
|
130
|
+
success: { glyph: '✅', label: 'passing' },
|
|
131
|
+
succeeded: { glyph: '✅', label: 'passing' },
|
|
132
|
+
passing: { glyph: '✅', label: 'passing' },
|
|
133
|
+
failing: { glyph: '❌', label: 'failing' },
|
|
134
|
+
failed: { glyph: '❌', label: 'failing' },
|
|
135
|
+
error: { glyph: '❌', label: 'failing' },
|
|
136
|
+
pending: { glyph: '⏳', label: 'pending' },
|
|
137
|
+
running: { glyph: '⏳', label: 'running' },
|
|
138
|
+
queued: { glyph: '⏳', label: 'queued' },
|
|
139
|
+
in_progress: { glyph: '⏳', label: 'in progress' },
|
|
140
|
+
};
|
|
141
|
+
var REVIEW_STATUS = {
|
|
142
|
+
approved: { glyph: '✅', label: 'approved' },
|
|
143
|
+
'changes-requested': { glyph: '❌', label: 'changes requested' },
|
|
144
|
+
changes_requested: { glyph: '❌', label: 'changes requested' },
|
|
145
|
+
'requested-changes': { glyph: '❌', label: 'changes requested' },
|
|
146
|
+
rejected: { glyph: '❌', label: 'rejected' },
|
|
147
|
+
pending: { glyph: '💬', label: 'pending' },
|
|
148
|
+
commented: { glyph: '💬', label: 'commented' },
|
|
149
|
+
};
|
|
150
|
+
function statusSpan(subsystem, raw, table) {
|
|
151
|
+
var info = table[String(raw || '').toLowerCase()];
|
|
152
|
+
var glyph = info ? info.glyph : '➖';
|
|
153
|
+
var label = info ? info.label : (raw ? String(raw) : 'none');
|
|
154
|
+
var span = document.createElement('span');
|
|
155
|
+
span.className = 'pr-status';
|
|
156
|
+
span.textContent = glyph + ' ' + subsystem + ' ' + label;
|
|
157
|
+
span.title = subsystem + ': ' + (raw || 'unknown');
|
|
158
|
+
return span;
|
|
159
|
+
}
|
|
86
160
|
var prs = Array.isArray(data.pullRequests) ? data.pullRequests : [];
|
|
87
161
|
if (!prs.length) { tileEmpty(body, 'No tracked pull requests.'); return; }
|
|
88
162
|
prs.forEach(function(p) {
|
|
89
163
|
var num = p.prNumber || p.number || p.id;
|
|
90
164
|
var build = p.buildStatus || '';
|
|
165
|
+
var review = p.reviewStatus || '';
|
|
91
166
|
var cls = p.status === 'merged' ? 'green'
|
|
92
167
|
: (build === 'failing' ? 'red'
|
|
93
168
|
: ((p.status === 'active' || p.status === 'linked') ? 'blue' : ''));
|
|
169
|
+
var statusText = document.createElement('span');
|
|
170
|
+
statusText.textContent = p.status || 'unknown';
|
|
94
171
|
body.appendChild(tileCard({
|
|
95
172
|
title: (num ? '#' + num + ' ' : '') + (p.title || '(untitled PR)'),
|
|
96
|
-
|
|
173
|
+
// Project slug first (under title), then build/review row below — the
|
|
174
|
+
// project is the strongest disambiguator across PRs and benefits from
|
|
175
|
+
// being adjacent to the title.
|
|
176
|
+
subtle: parsePrSlug(p.id),
|
|
177
|
+
subtleBeforeMeta: true,
|
|
178
|
+
metaNodes: [statusText, statusSpan('build', build, BUILD_STATUS), statusSpan('review', review, REVIEW_STATUS)],
|
|
97
179
|
chip: { text: p.status || '—', cls: cls },
|
|
98
180
|
href: p.url || null,
|
|
99
181
|
}));
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// Project context picker: populated from /api/status (which already returns
|
|
2
|
-
// the configured project list).
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
2
|
+
// the configured project list). One-or-more projects: a <select> dropdown
|
|
3
|
+
// whose first option is a "Select project" sentinel (value ""), italicized
|
|
4
|
+
// via CSS, so the indicator reads "Select project" until the user explicitly
|
|
5
|
+
// picks one and can always be cleared back to it. Zero-project: a hint to
|
|
6
|
+
// add one. No auto-default: we never silently fall back to projects[0] /
|
|
7
|
+
// last-used / constellation, so project-scoped CC actions can't land in the
|
|
8
|
+
// wrong project (W-mqayzsj3). Selection persists per-browser in localStorage
|
|
9
|
+
// and is included in every /api/command-center/stream call.
|
|
6
10
|
// Build the "+ Add" button as a real DOM node (was an HTML string + innerHTML
|
|
7
11
|
// injection — SEC-03 ratchet prefers we avoid that pattern, see
|
|
8
12
|
// test/unit.test.js DYNAMIC_INNERHTML_BASELINE).
|
|
@@ -22,19 +26,20 @@
|
|
|
22
26
|
empty.textContent = 'No projects linked yet.';
|
|
23
27
|
return empty;
|
|
24
28
|
}
|
|
25
|
-
// Strip variant:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return staticSpan;
|
|
31
|
-
}
|
|
32
|
-
// Strip variant: multi-project <select>. Returns the select so the caller
|
|
33
|
-
// can attach a change listener.
|
|
29
|
+
// Strip variant: project <select>. The first option is a "Select project"
|
|
30
|
+
// sentinel (value "") that is selected whenever no project is explicitly
|
|
31
|
+
// chosen, so the indicator never auto-defaults and the user can always clear
|
|
32
|
+
// their choice back to it. Returns the select so the caller can attach a
|
|
33
|
+
// change listener.
|
|
34
34
|
function makeContextSelect(projects, selected) {
|
|
35
35
|
var sel = document.createElement('select');
|
|
36
36
|
sel.id = 'chat-context-select';
|
|
37
37
|
sel.setAttribute('aria-label', 'Working in project');
|
|
38
|
+
var placeholder = document.createElement('option');
|
|
39
|
+
placeholder.value = '';
|
|
40
|
+
placeholder.textContent = 'Select project';
|
|
41
|
+
if (!selected) placeholder.selected = true;
|
|
42
|
+
sel.appendChild(placeholder);
|
|
38
43
|
for (var i = 0; i < projects.length; i++) {
|
|
39
44
|
var p = projects[i];
|
|
40
45
|
var opt = document.createElement('option');
|
|
@@ -69,22 +74,25 @@
|
|
|
69
74
|
currentProject = null;
|
|
70
75
|
try { localStorage.removeItem(SLIM_PROJECT_KEY); } catch (_e) {}
|
|
71
76
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
currentProject = ev.target.value || null;
|
|
77
|
+
// No auto-default: when nothing is explicitly selected, currentProject
|
|
78
|
+
// stays null and the picker shows the "Select project" sentinel
|
|
79
|
+
// (italicized via CSS). We deliberately do NOT fall back to projects[0]
|
|
80
|
+
// here (W-mqayzsj3) so CC turns and project-scoped actions don't
|
|
81
|
+
// silently target the wrong project.
|
|
82
|
+
var sel = makeContextSelect(projects, currentProject);
|
|
83
|
+
stripEl.style.display = '';
|
|
84
|
+
controlsEl.replaceChildren(sel, makeContextAddBtn());
|
|
85
|
+
sel.addEventListener('change', function(ev) {
|
|
86
|
+
var val = ev.target.value || '';
|
|
87
|
+
if (val) {
|
|
88
|
+
currentProject = val;
|
|
85
89
|
try { localStorage.setItem(SLIM_PROJECT_KEY, currentProject); } catch (_e) {}
|
|
86
|
-
}
|
|
87
|
-
|
|
90
|
+
} else {
|
|
91
|
+
// User cleared back to "Select project" — drop the persisted choice.
|
|
92
|
+
currentProject = null;
|
|
93
|
+
try { localStorage.removeItem(SLIM_PROJECT_KEY); } catch (_e) {}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
88
96
|
}
|
|
89
97
|
var addBtn = document.getElementById('chat-context-add');
|
|
90
98
|
if (addBtn) addBtn.addEventListener('click', openAddProjectModal);
|
|
@@ -16,6 +16,13 @@
|
|
|
16
16
|
}, delay || 0);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// Tiles whose numeric value should fire a one-shot cockpit-flash animation
|
|
20
|
+
// whenever it changes (W-mqb02bga: status panel). Suppressed on first paint
|
|
21
|
+
// — only deltas from a previously-seen number trigger the flash, so opening
|
|
22
|
+
// the dashboard doesn't pulse every tile.
|
|
23
|
+
var FLASH_ON_CHANGE_KEYS = { dispatches: true, prs: true };
|
|
24
|
+
var lastTileValues = Object.create(null);
|
|
25
|
+
|
|
19
26
|
// Apply lit-state and value/detail text to a tile.
|
|
20
27
|
function updateTile(key, value, detail, lit) {
|
|
21
28
|
var tile = document.querySelector('.cockpit-tile[data-tile="' + key + '"]');
|
|
@@ -27,16 +34,57 @@
|
|
|
27
34
|
valEl.classList.toggle('dim', !lit);
|
|
28
35
|
}
|
|
29
36
|
if (detEl) detEl.textContent = detail || '';
|
|
30
|
-
tile.classList.remove('lit-blue', 'lit-green', 'lit-amber', 'lit-red');
|
|
37
|
+
tile.classList.remove('lit-blue', 'lit-green', 'lit-amber', 'lit-orange', 'lit-red');
|
|
31
38
|
if (lit) tile.classList.add('lit-' + lit);
|
|
39
|
+
// Flash the tile when its number changes. Remove → force reflow → re-add
|
|
40
|
+
// restarts the animation even when the class was already present from the
|
|
41
|
+
// previous tick (otherwise repeated changes would only fire on the first
|
|
42
|
+
// delta after class application). Only numeric values count as a change;
|
|
43
|
+
// ignore null/undefined transitions.
|
|
44
|
+
if (FLASH_ON_CHANGE_KEYS[key] && typeof value === 'number') {
|
|
45
|
+
var prev = lastTileValues[key];
|
|
46
|
+
if (typeof prev === 'number' && prev !== value) {
|
|
47
|
+
tile.classList.remove('flash-change');
|
|
48
|
+
void tile.offsetWidth;
|
|
49
|
+
tile.classList.add('flash-change');
|
|
50
|
+
}
|
|
51
|
+
lastTileValues[key] = value;
|
|
52
|
+
}
|
|
32
53
|
}
|
|
33
54
|
|
|
34
55
|
// Latest /api/status snapshot — read by the cockpit-tile detail modals.
|
|
35
56
|
var lastStatusData = null;
|
|
36
57
|
|
|
58
|
+
// Reflect the Command Center's resolved runtime + model in the Actions panel
|
|
59
|
+
// header (slim-ux only — classic chrome is untouched). Source is the same
|
|
60
|
+
// /api/status payload the cockpit polls: autoMode.ccModel (config.engine
|
|
61
|
+
// ccModel → defaultModel → runtime default) and autoMode.ccCli. When no model
|
|
62
|
+
// is resolvable (runtime picks its own default), the badge stays hidden rather
|
|
63
|
+
// than guessing. textContent keeps it XSS-safe — the values come from config.
|
|
64
|
+
function updateCcModelBadge(model, cli) {
|
|
65
|
+
var badge = document.getElementById('cc-model-badge');
|
|
66
|
+
if (!badge) return;
|
|
67
|
+
var m = (model === null || model === undefined) ? '' : String(model).trim();
|
|
68
|
+
if (!m) {
|
|
69
|
+
badge.hidden = true;
|
|
70
|
+
badge.textContent = '';
|
|
71
|
+
badge.removeAttribute('title');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
var c = (cli === null || cli === undefined) ? '' : String(cli).trim();
|
|
75
|
+
var label = c ? (c + ' · ' + m) : m;
|
|
76
|
+
badge.textContent = label;
|
|
77
|
+
badge.title = 'Command Center model: ' + label;
|
|
78
|
+
badge.hidden = false;
|
|
79
|
+
}
|
|
80
|
+
|
|
37
81
|
function applyStatus(data) {
|
|
38
82
|
if (!data) return;
|
|
39
83
|
lastStatusData = data;
|
|
84
|
+
// ── Command Center model badge (Actions panel header) ──────
|
|
85
|
+
var autoMode = data.autoMode || {};
|
|
86
|
+
updateCcModelBadge(autoMode.ccModel, autoMode.ccCli);
|
|
87
|
+
|
|
40
88
|
// ── Engine tile ────────────────────────────────────────────
|
|
41
89
|
var engine = data.engine || {};
|
|
42
90
|
// /api/status exposes `state` ('running'|'paused'|'stopping'|'stopped')
|
|
@@ -65,14 +113,14 @@
|
|
|
65
113
|
updateTile(
|
|
66
114
|
'dispatches',
|
|
67
115
|
active.length,
|
|
68
|
-
active.length ? '
|
|
69
|
-
active.length ? '
|
|
116
|
+
active.length ? 'agents working' : 'no agents working',
|
|
117
|
+
active.length ? 'amber' : null
|
|
70
118
|
);
|
|
71
119
|
updateTile(
|
|
72
120
|
'queued',
|
|
73
121
|
pending.length,
|
|
74
122
|
pending.length ? 'waiting to spawn' : 'queue empty',
|
|
75
|
-
pending.length ? '
|
|
123
|
+
pending.length ? 'orange' : null
|
|
76
124
|
);
|
|
77
125
|
|
|
78
126
|
// ── PR tile (status snapshot from cached pull-requests) ─────
|
|
@@ -20,9 +20,15 @@
|
|
|
20
20
|
--blue: #58a6ff;
|
|
21
21
|
--green: #3fb950;
|
|
22
22
|
--amber: #d29922;
|
|
23
|
+
--orange: #ea580c;
|
|
23
24
|
--red: #f85149;
|
|
24
25
|
--radius: 6px;
|
|
25
26
|
|
|
27
|
+
/* Spacing scale (W-mqazlkym) — 4/8/12/16/20 rhythm for the chat
|
|
28
|
+
surface. Slim ships its CSS independently, so the scale is declared
|
|
29
|
+
here rather than reusing dashboard/styles.css's --space-* tokens. */
|
|
30
|
+
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px;
|
|
31
|
+
|
|
26
32
|
/* Typography — mirrors dashboard/styles.css :root tokens
|
|
27
33
|
(W-mq1c8og40003a7cf). Slim ships its CSS independently
|
|
28
34
|
(template-baked into slim/layout.html), so we duplicate the
|
|
@@ -221,6 +227,34 @@
|
|
|
221
227
|
color: var(--muted);
|
|
222
228
|
font-size: var(--text-base);
|
|
223
229
|
}
|
|
230
|
+
/* Right-aligned cluster in the Actions panel header: keeps the CC model
|
|
231
|
+
badge tucked next to the "Command Center" sub-label without disturbing
|
|
232
|
+
the header's space-between layout. */
|
|
233
|
+
.panel-header .panel-head-meta {
|
|
234
|
+
display: flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
gap: 8px;
|
|
237
|
+
min-width: 0;
|
|
238
|
+
}
|
|
239
|
+
/* Compact pill surfacing the Command Center's resolved runtime/model
|
|
240
|
+
(e.g. "copilot · gpt-5.4"). Muted, monospace, low-key — visible but
|
|
241
|
+
unobtrusive. Populated by status.js#updateCcModelBadge. */
|
|
242
|
+
.cc-model-badge {
|
|
243
|
+
text-transform: none;
|
|
244
|
+
letter-spacing: 0;
|
|
245
|
+
font-weight: 500;
|
|
246
|
+
font-size: var(--text-sm);
|
|
247
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
248
|
+
color: var(--muted);
|
|
249
|
+
background: var(--surface2);
|
|
250
|
+
border: 1px solid var(--border);
|
|
251
|
+
border-radius: 999px;
|
|
252
|
+
padding: 1px 8px;
|
|
253
|
+
white-space: nowrap;
|
|
254
|
+
overflow: hidden;
|
|
255
|
+
text-overflow: ellipsis;
|
|
256
|
+
max-width: 220px;
|
|
257
|
+
}
|
|
224
258
|
.panel-body {
|
|
225
259
|
flex: 1;
|
|
226
260
|
overflow-y: auto;
|
|
@@ -252,9 +286,9 @@
|
|
|
252
286
|
.chat-tabs {
|
|
253
287
|
display: flex;
|
|
254
288
|
align-items: center;
|
|
255
|
-
gap:
|
|
289
|
+
gap: var(--space-2);
|
|
256
290
|
flex-wrap: wrap;
|
|
257
|
-
padding:
|
|
291
|
+
padding: var(--space-2) var(--space-3) 0;
|
|
258
292
|
width: 100%;
|
|
259
293
|
max-width: 800px;
|
|
260
294
|
margin-left: auto;
|
|
@@ -310,10 +344,13 @@
|
|
|
310
344
|
.chat-messages {
|
|
311
345
|
flex: 1;
|
|
312
346
|
overflow-y: auto;
|
|
313
|
-
padding
|
|
347
|
+
/* Extra top padding so the first message clears the tab/context chip
|
|
348
|
+
row above it (#10); 12px sides keep the left grid line shared with the
|
|
349
|
+
input row and action toolbar (#3). */
|
|
350
|
+
padding: var(--space-4) var(--space-3) var(--space-3);
|
|
314
351
|
display: flex;
|
|
315
352
|
flex-direction: column;
|
|
316
|
-
gap:
|
|
353
|
+
gap: var(--space-3);
|
|
317
354
|
min-height: 0;
|
|
318
355
|
width: 100%;
|
|
319
356
|
max-width: 800px;
|
|
@@ -329,7 +366,9 @@
|
|
|
329
366
|
}
|
|
330
367
|
.chat-msg {
|
|
331
368
|
max-width: 90%;
|
|
332
|
-
padding
|
|
369
|
+
/* Uniform bubble padding across variants (#2); 20px horizontal gives
|
|
370
|
+
wrapped technical text >=16px right-edge clearance (#8). */
|
|
371
|
+
padding: var(--space-4) var(--space-5);
|
|
333
372
|
border-radius: var(--radius);
|
|
334
373
|
white-space: pre-wrap;
|
|
335
374
|
word-wrap: break-word;
|
|
@@ -415,8 +454,10 @@
|
|
|
415
454
|
.chat-queue-resume:hover { filter: brightness(1.1); }
|
|
416
455
|
.chat-input-wrap {
|
|
417
456
|
display: flex;
|
|
418
|
-
gap:
|
|
419
|
-
|
|
457
|
+
gap: var(--space-3);
|
|
458
|
+
/* Breathing room around the textarea; the larger gap separates it from
|
|
459
|
+
the Send/Stop buttons (#5). */
|
|
460
|
+
padding: var(--space-3);
|
|
420
461
|
border-top: 1px solid var(--border);
|
|
421
462
|
flex-shrink: 0;
|
|
422
463
|
background: var(--surface);
|
|
@@ -433,8 +474,8 @@
|
|
|
433
474
|
.chat-context-strip {
|
|
434
475
|
display: flex;
|
|
435
476
|
align-items: center;
|
|
436
|
-
gap:
|
|
437
|
-
padding:
|
|
477
|
+
gap: var(--space-3);
|
|
478
|
+
padding: var(--space-2) var(--space-3);
|
|
438
479
|
border-top: 1px solid var(--border);
|
|
439
480
|
flex-shrink: 0;
|
|
440
481
|
background: var(--surface);
|
|
@@ -462,6 +503,16 @@
|
|
|
462
503
|
cursor: pointer;
|
|
463
504
|
}
|
|
464
505
|
.chat-context-strip select:focus { outline: none; border-color: var(--blue); }
|
|
506
|
+
/* Italicize the "Select project" placeholder option AND the closed select
|
|
507
|
+
widget while the placeholder is the active value. `:has()` is needed
|
|
508
|
+
because native <select> rendering ignores child <option> styles for the
|
|
509
|
+
displayed (closed) text — we have to style the <select> itself. The
|
|
510
|
+
explicit `option { font-style: normal }` reset stops real project names
|
|
511
|
+
from inheriting the parent select's italic when the dropdown is opened
|
|
512
|
+
while the placeholder is still selected. */
|
|
513
|
+
.chat-context-strip select option { font-style: normal; color: var(--text); }
|
|
514
|
+
.chat-context-strip select option[value=""] { font-style: italic; color: var(--muted); }
|
|
515
|
+
.chat-context-strip select:has(option[value=""]:checked) { font-style: italic; color: var(--muted); }
|
|
465
516
|
.chat-context-strip .context-static {
|
|
466
517
|
color: var(--text);
|
|
467
518
|
font-weight: 500;
|
|
@@ -514,17 +565,19 @@
|
|
|
514
565
|
.chat-send:hover { filter: brightness(1.1); }
|
|
515
566
|
.chat-send:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
516
567
|
.chat-stop {
|
|
517
|
-
padding:
|
|
568
|
+
padding: 9px;
|
|
518
569
|
background: var(--surface2);
|
|
519
570
|
color: var(--red);
|
|
520
571
|
border: 1px solid var(--border);
|
|
521
572
|
border-radius: var(--radius);
|
|
522
573
|
font-size: var(--text-md);
|
|
574
|
+
line-height: 0;
|
|
523
575
|
cursor: pointer;
|
|
524
576
|
align-self: flex-end;
|
|
525
577
|
display: none;
|
|
526
578
|
}
|
|
527
579
|
.chat-stop:hover { filter: brightness(1.1); }
|
|
580
|
+
.chat-stop .chat-stop-icon { display: block; width: 20px; height: 20px; }
|
|
528
581
|
|
|
529
582
|
/* Tool-call progress panel + thinking indicator. While streaming, the
|
|
530
583
|
thinking indicator sits at the bottom-left of the response card and the
|
|
@@ -534,7 +587,7 @@
|
|
|
534
587
|
display: flex;
|
|
535
588
|
align-items: flex-start;
|
|
536
589
|
gap: 12px;
|
|
537
|
-
margin-top:
|
|
590
|
+
margin-top: var(--space-3);
|
|
538
591
|
}
|
|
539
592
|
/* Collapsed tool panel: at most 3 non-wrapping lines, scrolls (newest at
|
|
540
593
|
the bottom) once there are more. Clicking opens the full list in a modal. */
|
|
@@ -614,6 +667,8 @@
|
|
|
614
667
|
min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
615
668
|
}
|
|
616
669
|
.tile-item-meta { font-size: var(--text-base); color: var(--muted); margin-top: 3px; word-break: break-word; }
|
|
670
|
+
.tile-item-subtle { font-size: var(--text-sm); color: var(--muted); margin-top: 2px; word-break: break-word; }
|
|
671
|
+
.pr-status { display: inline-block; margin: 0 1px; text-decoration: none; }
|
|
617
672
|
.tile-chip {
|
|
618
673
|
flex: 0 0 auto;
|
|
619
674
|
font-size: var(--text-base); font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase;
|
|
@@ -676,12 +731,26 @@
|
|
|
676
731
|
|
|
677
732
|
.chat-action {
|
|
678
733
|
align-self: flex-start;
|
|
679
|
-
padding:
|
|
734
|
+
padding: var(--space-1) var(--space-3);
|
|
680
735
|
border-radius: 4px;
|
|
681
736
|
font-size: var(--text-base);
|
|
682
737
|
border: 1px dashed var(--border);
|
|
683
738
|
color: var(--muted);
|
|
684
739
|
}
|
|
740
|
+
/* A status line that immediately follows a message reads as that card's
|
|
741
|
+
output (#9): tighten the gap (negative margin trims the 12px column gap
|
|
742
|
+
to ~4px), drop the free-floating dashed box, and tie it to the card
|
|
743
|
+
above with a shared left edge + left-border continuation. */
|
|
744
|
+
.chat-msg + .chat-action {
|
|
745
|
+
margin-top: calc(-1 * var(--space-2));
|
|
746
|
+
border: none;
|
|
747
|
+
border-left: 2px solid var(--border);
|
|
748
|
+
border-radius: 0 var(--radius) var(--radius) 0;
|
|
749
|
+
padding-left: var(--space-3);
|
|
750
|
+
}
|
|
751
|
+
.chat-msg + .chat-action.ok { border-left-color: var(--green); }
|
|
752
|
+
.chat-msg + .chat-action.warn { border-left-color: var(--amber); }
|
|
753
|
+
.chat-msg + .chat-action.err { border-left-color: var(--red); }
|
|
685
754
|
.chat-action.ok { color: var(--green); }
|
|
686
755
|
.chat-action.warn { color: var(--amber); }
|
|
687
756
|
.chat-action.err { color: var(--red); }
|
|
@@ -693,9 +762,10 @@
|
|
|
693
762
|
}
|
|
694
763
|
.chat-msg.assistant code {
|
|
695
764
|
background: var(--bg);
|
|
696
|
-
padding:
|
|
765
|
+
padding: 2px 6px;
|
|
697
766
|
border-radius: 3px;
|
|
698
767
|
font-size: var(--text-code);
|
|
768
|
+
vertical-align: baseline;
|
|
699
769
|
}
|
|
700
770
|
.chat-msg.assistant pre {
|
|
701
771
|
background: var(--bg);
|
|
@@ -709,13 +779,16 @@
|
|
|
709
779
|
.chat-msg.assistant a { color: var(--blue); }
|
|
710
780
|
.chat-msg.assistant strong { color: var(--text); }
|
|
711
781
|
|
|
712
|
-
/* ── Action footer:
|
|
782
|
+
/* ── Action footer: Link PR + Pin Content ──
|
|
783
|
+
De-emphasized (#6, option b): ghost/secondary styling so they no longer
|
|
784
|
+
compete with the primary blue Send button. The 4-column grid keeps each
|
|
785
|
+
button at a quarter of the panel width (low visual weight). */
|
|
713
786
|
.actions-buttons {
|
|
714
787
|
flex-shrink: 0;
|
|
715
788
|
display: grid;
|
|
716
789
|
grid-template-columns: repeat(4, 1fr);
|
|
717
|
-
gap:
|
|
718
|
-
padding:
|
|
790
|
+
gap: var(--space-2);
|
|
791
|
+
padding: var(--space-3);
|
|
719
792
|
border-top: 1px solid var(--border);
|
|
720
793
|
width: 100%;
|
|
721
794
|
max-width: 800px;
|
|
@@ -723,11 +796,11 @@
|
|
|
723
796
|
margin-right: auto;
|
|
724
797
|
}
|
|
725
798
|
.link-pr-btn {
|
|
726
|
-
background:
|
|
727
|
-
color: var(--
|
|
799
|
+
background: transparent;
|
|
800
|
+
color: var(--muted);
|
|
728
801
|
border: 1px solid var(--border);
|
|
729
802
|
border-radius: var(--radius);
|
|
730
|
-
padding:
|
|
803
|
+
padding: var(--space-2) var(--space-3);
|
|
731
804
|
cursor: pointer;
|
|
732
805
|
font-size: var(--text-md);
|
|
733
806
|
font-family: inherit;
|
|
@@ -793,9 +866,16 @@
|
|
|
793
866
|
table inside the IIFE. */
|
|
794
867
|
.cockpit-grid {
|
|
795
868
|
display: grid;
|
|
796
|
-
grid-template-columns: repeat(
|
|
869
|
+
grid-template-columns: repeat(6, 1fr);
|
|
797
870
|
gap: 8px;
|
|
798
871
|
}
|
|
872
|
+
/* Row 2: Queued | Dispatches | PRs — three equal columns (2 of 6 each). */
|
|
873
|
+
.cockpit-grid > [data-tile="queued"],
|
|
874
|
+
.cockpit-grid > [data-tile="dispatches"],
|
|
875
|
+
.cockpit-grid > [data-tile="prs"] { grid-column: span 2; }
|
|
876
|
+
/* Row 3: Watches | Pinned — two equal columns (3 of 6 each). */
|
|
877
|
+
.cockpit-grid > [data-tile="watches"],
|
|
878
|
+
.cockpit-grid > [data-tile="pinned"] { grid-column: span 3; }
|
|
799
879
|
.cockpit-tile {
|
|
800
880
|
background: var(--surface2);
|
|
801
881
|
border: 1px solid var(--border);
|
|
@@ -805,22 +885,37 @@
|
|
|
805
885
|
flex-direction: column;
|
|
806
886
|
gap: 4px;
|
|
807
887
|
transition: border-color 0.2s, background 0.2s;
|
|
888
|
+
/* Per-state flash color (rgb triplet, no rgba wrapper) consumed by
|
|
889
|
+
`cockpit-flash` so the change-flash ring matches the tile's lit state
|
|
890
|
+
(amber for working dispatches, orange for queued, red for failing
|
|
891
|
+
PRs, etc.). Each lit-* class below overrides this. Default is blue
|
|
892
|
+
for unlit / generic tiles. */
|
|
893
|
+
--flash-color: 88, 166, 255;
|
|
808
894
|
}
|
|
809
895
|
.cockpit-tile.lit-blue {
|
|
810
896
|
border-color: rgba(88, 166, 255, 0.6);
|
|
811
897
|
background: rgba(88, 166, 255, 0.06);
|
|
898
|
+
--flash-color: 88, 166, 255;
|
|
812
899
|
}
|
|
813
900
|
.cockpit-tile.lit-green {
|
|
814
901
|
border-color: rgba(63, 185, 80, 0.6);
|
|
815
902
|
background: rgba(63, 185, 80, 0.06);
|
|
903
|
+
--flash-color: 63, 185, 80;
|
|
816
904
|
}
|
|
817
905
|
.cockpit-tile.lit-amber {
|
|
818
906
|
border-color: rgba(210, 153, 34, 0.6);
|
|
819
907
|
background: rgba(210, 153, 34, 0.06);
|
|
908
|
+
--flash-color: 210, 153, 34;
|
|
909
|
+
}
|
|
910
|
+
.cockpit-tile.lit-orange {
|
|
911
|
+
border-color: rgba(234, 88, 12, 0.6);
|
|
912
|
+
background: rgba(234, 88, 12, 0.08);
|
|
913
|
+
--flash-color: 234, 88, 12;
|
|
820
914
|
}
|
|
821
915
|
.cockpit-tile.lit-red {
|
|
822
916
|
border-color: rgba(248, 81, 73, 0.6);
|
|
823
917
|
background: rgba(248, 81, 73, 0.08);
|
|
918
|
+
--flash-color: 248, 81, 73;
|
|
824
919
|
}
|
|
825
920
|
.cockpit-label {
|
|
826
921
|
font-size: var(--text-base);
|
|
@@ -842,6 +937,7 @@
|
|
|
842
937
|
.lit-blue .cockpit-dot { background: var(--blue); box-shadow: 0 0 6px var(--blue); }
|
|
843
938
|
.lit-green .cockpit-dot { background: var(--green); box-shadow: 0 0 6px var(--green); }
|
|
844
939
|
.lit-amber .cockpit-dot { background: var(--amber); box-shadow: 0 0 6px var(--amber); }
|
|
940
|
+
.lit-orange .cockpit-dot { background: var(--orange); box-shadow: 0 0 6px var(--orange); }
|
|
845
941
|
.lit-red .cockpit-dot { background: var(--red); box-shadow: 0 0 6px var(--red); }
|
|
846
942
|
.cockpit-value {
|
|
847
943
|
font-size: var(--text-stat);
|
|
@@ -856,10 +952,59 @@
|
|
|
856
952
|
line-height: 1.3;
|
|
857
953
|
min-height: 14px;
|
|
858
954
|
}
|
|
955
|
+
/* Engine tile: full-width, compact single-line horizontal strip. */
|
|
956
|
+
.cockpit-tile--full {
|
|
957
|
+
grid-column: 1 / -1;
|
|
958
|
+
flex-direction: row;
|
|
959
|
+
align-items: center;
|
|
960
|
+
gap: 16px;
|
|
961
|
+
padding: 7px 12px;
|
|
962
|
+
}
|
|
963
|
+
.cockpit-tile--full .cockpit-value { font-size: var(--text-xl); }
|
|
964
|
+
.cockpit-tile--full .cockpit-detail {
|
|
965
|
+
min-height: 0;
|
|
966
|
+
margin-left: auto;
|
|
967
|
+
text-align: right;
|
|
968
|
+
}
|
|
859
969
|
/* Cockpit tiles are clickable -> detail modal. */
|
|
860
970
|
.cockpit-tile { cursor: pointer; position: relative; }
|
|
861
971
|
.cockpit-tile:hover { border-color: var(--blue); }
|
|
862
972
|
.cockpit-tile:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }
|
|
973
|
+
/* Active Dispatches tile pulses while there is work in flight — same
|
|
974
|
+
cadence as the per-member "working" badge in the Team section, so the
|
|
975
|
+
eye instantly ties "things are happening" in both regions. The pulse
|
|
976
|
+
only runs in the lit-amber state (dispatches > 0); empty state is
|
|
977
|
+
static. Respects prefers-reduced-motion. */
|
|
978
|
+
.cockpit-tile[data-tile="dispatches"].lit-amber {
|
|
979
|
+
animation: slim-pulse 1.6s ease-in-out infinite;
|
|
980
|
+
}
|
|
981
|
+
/* Change-flash: brief one-shot ring around a tile whose value just
|
|
982
|
+
changed, used by status.js for the `dispatches` and `prs` tiles. Layered
|
|
983
|
+
on a pseudo-element so it composes cleanly with `slim-pulse` above
|
|
984
|
+
(running both on the tile itself would force a single `animation:` rule
|
|
985
|
+
and clobber one of them). The ring expands then fades — fully visible
|
|
986
|
+
even when the underlying tile is already lit. Respects
|
|
987
|
+
prefers-reduced-motion. */
|
|
988
|
+
.cockpit-tile::after {
|
|
989
|
+
content: '';
|
|
990
|
+
position: absolute;
|
|
991
|
+
inset: -1px;
|
|
992
|
+
border-radius: inherit;
|
|
993
|
+
pointer-events: none;
|
|
994
|
+
box-shadow: 0 0 0 0 rgba(var(--flash-color), 0);
|
|
995
|
+
}
|
|
996
|
+
.cockpit-tile.flash-change::after {
|
|
997
|
+
animation: cockpit-flash 0.75s ease-out;
|
|
998
|
+
}
|
|
999
|
+
@keyframes cockpit-flash {
|
|
1000
|
+
0% { box-shadow: 0 0 0 0 rgba(var(--flash-color), 0); }
|
|
1001
|
+
30% { box-shadow: 0 0 0 6px rgba(var(--flash-color), 0.7); }
|
|
1002
|
+
100% { box-shadow: 0 0 0 0 rgba(var(--flash-color), 0); }
|
|
1003
|
+
}
|
|
1004
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1005
|
+
.cockpit-tile[data-tile="dispatches"].lit-amber { animation: none; }
|
|
1006
|
+
.cockpit-tile.flash-change::after { animation: none; }
|
|
1007
|
+
}
|
|
863
1008
|
|
|
864
1009
|
/* ── Status panel sub-divisions: Team cards over System tiles,
|
|
865
1010
|
separated by a divider line (no text headers). ──── */
|
package/dashboard.js
CHANGED
|
@@ -8865,13 +8865,20 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
8865
8865
|
// `currentProject` hint so CC can default action.project without the
|
|
8866
8866
|
// user naming it every turn. Validate against the configured project
|
|
8867
8867
|
// list before injecting — body.currentProject is untrusted input.
|
|
8868
|
+
// When slim has no project explicitly selected (the "Select project"
|
|
8869
|
+
// indicator), it instead sends `noProjectSelected: true` so we inject an
|
|
8870
|
+
// explicit "do not default — ask the user" preamble rather than silently
|
|
8871
|
+
// letting CC pick projects[0] (W-mqayzsj3). The classic dashboard sends
|
|
8872
|
+
// neither field, so its prompt stays byte-identical (empty part).
|
|
8868
8873
|
const _knownProjectNames = (PROJECTS || []).map(p => p && p.name).filter(Boolean);
|
|
8869
8874
|
const _currentProject = (body.currentProject && _knownProjectNames.includes(body.currentProject))
|
|
8870
8875
|
? body.currentProject
|
|
8871
8876
|
: null;
|
|
8872
8877
|
const projectContextPart = _currentProject
|
|
8873
8878
|
? `[Project Context] The user is currently working in project "${_currentProject}". Default any action's "project" field to this project unless the user explicitly names a different one.`
|
|
8874
|
-
:
|
|
8879
|
+
: (body.noProjectSelected
|
|
8880
|
+
? `[Project Context] No project is selected. Do NOT default any action's "project" field — ask the user to pick a project before creating or dispatching project-scoped work.`
|
|
8881
|
+
: '');
|
|
8875
8882
|
const prompt = _joinCcPromptParts(preamble, resumeGuard, carryover, turnHeader, projectContextPart, body.message);
|
|
8876
8883
|
|
|
8877
8884
|
const { trackEngineUsage: trackUsage, trackEngineError: trackErr, withCcTurnTimeout: withTimeout } = require('./engine/llm');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2177",
|
|
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"
|