@yemi33/minions 0.1.2309 → 0.1.2310
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/refresh.js +2 -1
- package/dashboard/js/render-other.js +56 -21
- package/dashboard/styles.css +10 -4
- package/dashboard.js +4 -1
- package/engine/lifecycle.js +53 -31
- package/engine/shared.js +47 -10
- package/package.json +1 -1
- package/prompts/cc-system.md +1 -1
package/dashboard/js/refresh.js
CHANGED
|
@@ -135,7 +135,8 @@ const RENDER_VERSIONS = {
|
|
|
135
135
|
prdPrs: 1,
|
|
136
136
|
inbox: 2,
|
|
137
137
|
// Bumped 4→5 for the clickable checkout-mode pill + picker (W-mr1b67zi0006b788).
|
|
138
|
-
|
|
138
|
+
// Bumped 5→6 for multi-select hybrid liveValidation.type support (W-mr2m1ute000a9c01).
|
|
139
|
+
projects: 6,
|
|
139
140
|
notes: 1,
|
|
140
141
|
prd: 3,
|
|
141
142
|
prs: 3,
|
|
@@ -108,9 +108,11 @@ function _renderWorktreeModePill(p) {
|
|
|
108
108
|
const name = escHtml(p.name);
|
|
109
109
|
const common = ' data-checkout-pill="' + name + '" role="button" tabindex="0" aria-haspopup="true" aria-label="Change checkout mode"';
|
|
110
110
|
if (p.checkoutMode === 'live') {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
const types = _liveValidationTypesArray(p);
|
|
112
|
+
if (types.length > 0) {
|
|
113
|
+
const typesText = types.join(', ');
|
|
114
|
+
const vt = escapeHtml(typesText);
|
|
115
|
+
return ' <span class="project-mode-pill project-mode-hybrid project-mode-pill-clickable"' + common + ' title="Hybrid live-validation — coding work items author in isolated worktrees; only the "' + vt + '" validation type(s) run in-place on the live checkout (capped to one mutating dispatch, refused on a dirty tree). Click to change.">⚡ Hybrid · ' + vt + '</span>';
|
|
114
116
|
}
|
|
115
117
|
return ' <span class="project-mode-pill project-mode-live project-mode-pill-clickable"' + common + ' title="Live-checkout dispatch mode — agents run in-place inside the project working tree (no isolated worktree); capped to one mutating dispatch and refused on a dirty tree. Click to change.">⚡ Live checkout</span>';
|
|
116
118
|
}
|
|
@@ -130,6 +132,16 @@ function _findProjectInLastStatus(name) {
|
|
|
130
132
|
return projects.find(function(p) { return p.name === name; }) || null;
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
// Normalize p.liveValidationType (single string legacy shape, OR array of
|
|
136
|
+
// strings — W-mr2m1ute000a9c01 multi-select) into an array of non-empty
|
|
137
|
+
// strings for uniform handling by menu/pill rendering code.
|
|
138
|
+
function _liveValidationTypesArray(p) {
|
|
139
|
+
const raw = p && p.liveValidationType;
|
|
140
|
+
if (!raw) return [];
|
|
141
|
+
const arr = Array.isArray(raw) ? raw : [raw];
|
|
142
|
+
return arr.filter(function(t) { return typeof t === 'string' && t.length > 0; });
|
|
143
|
+
}
|
|
144
|
+
|
|
133
145
|
function _closeCheckoutModeMenu() {
|
|
134
146
|
const existing = document.getElementById('checkout-mode-menu');
|
|
135
147
|
if (existing) existing.remove();
|
|
@@ -183,8 +195,8 @@ function _openCheckoutModeMenu(projectName, anchorEl) {
|
|
|
183
195
|
item.appendChild(desc);
|
|
184
196
|
}
|
|
185
197
|
const isActive = (mode === 'worktree' && project.checkoutMode !== 'live')
|
|
186
|
-
|| (mode === 'live' && project.checkoutMode === 'live' &&
|
|
187
|
-
|| (mode === 'hybrid' && project.checkoutMode === 'live' &&
|
|
198
|
+
|| (mode === 'live' && project.checkoutMode === 'live' && _liveValidationTypesArray(project).length === 0)
|
|
199
|
+
|| (mode === 'hybrid' && project.checkoutMode === 'live' && _liveValidationTypesArray(project).length > 0);
|
|
188
200
|
if (isActive) {
|
|
189
201
|
item.setAttribute('aria-checked', 'true');
|
|
190
202
|
item.classList.add('checkout-mode-menu-item-active');
|
|
@@ -221,9 +233,12 @@ function _openCheckoutModeMenu(projectName, anchorEl) {
|
|
|
221
233
|
if (first) first.focus();
|
|
222
234
|
}
|
|
223
235
|
|
|
224
|
-
// Second step of the picker for hybrid mode: pick which work-item type
|
|
236
|
+
// Second step of the picker for hybrid mode: pick which work-item type(s) run
|
|
225
237
|
// in-place on the live checkout while everything else authors in isolated
|
|
226
|
-
// worktrees. Renders a
|
|
238
|
+
// worktrees. Renders a checkbox list (multi-select, W-mr2m1ute000a9c01) +
|
|
239
|
+
// Back/Apply into the existing menu element. A checkbox list (rather than a
|
|
240
|
+
// native <select multiple>) keeps the existing "click one row" affordance
|
|
241
|
+
// discoverable while allowing more than one type to be selected at once.
|
|
227
242
|
function _renderCheckoutModeHybridStep(menu, projectName) {
|
|
228
243
|
const project = _findProjectInLastStatus(projectName);
|
|
229
244
|
while (menu.firstChild) menu.removeChild(menu.firstChild);
|
|
@@ -235,21 +250,31 @@ function _renderCheckoutModeHybridStep(menu, projectName) {
|
|
|
235
250
|
|
|
236
251
|
const label = document.createElement('div');
|
|
237
252
|
label.className = 'checkout-mode-menu-item';
|
|
238
|
-
label.textContent = 'Work-item type to validate live:';
|
|
253
|
+
label.textContent = 'Work-item type(s) to validate live:';
|
|
239
254
|
menu.appendChild(label);
|
|
240
255
|
|
|
241
|
-
const
|
|
242
|
-
|
|
256
|
+
const currentTypes = _liveValidationTypesArray(project);
|
|
257
|
+
const preselect = currentTypes.length > 0 ? currentTypes : [CHECKOUT_MODE_HYBRID_DEFAULT_TYPE];
|
|
258
|
+
|
|
259
|
+
const checkboxList = document.createElement('div');
|
|
260
|
+
checkboxList.className = 'checkout-mode-menu-checkboxes';
|
|
261
|
+
const checkboxes = [];
|
|
243
262
|
CHECKOUT_MODE_HYBRID_TYPES.forEach(function(t) {
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
263
|
+
const row = document.createElement('label');
|
|
264
|
+
row.className = 'checkout-mode-menu-checkbox-row';
|
|
265
|
+
const cb = document.createElement('input');
|
|
266
|
+
cb.type = 'checkbox';
|
|
267
|
+
cb.value = t;
|
|
268
|
+
cb.className = 'checkout-mode-menu-checkbox';
|
|
269
|
+
cb.checked = preselect.includes(t);
|
|
270
|
+
checkboxes.push(cb);
|
|
271
|
+
const text = document.createElement('span');
|
|
272
|
+
text.textContent = t;
|
|
273
|
+
row.appendChild(cb);
|
|
274
|
+
row.appendChild(text);
|
|
275
|
+
checkboxList.appendChild(row);
|
|
248
276
|
});
|
|
249
|
-
|
|
250
|
-
? project.liveValidationType
|
|
251
|
-
: CHECKOUT_MODE_HYBRID_DEFAULT_TYPE;
|
|
252
|
-
menu.appendChild(select);
|
|
277
|
+
menu.appendChild(checkboxList);
|
|
253
278
|
|
|
254
279
|
const actions = document.createElement('div');
|
|
255
280
|
actions.className = 'checkout-mode-menu-actions';
|
|
@@ -270,9 +295,10 @@ function _renderCheckoutModeHybridStep(menu, projectName) {
|
|
|
270
295
|
applyBtn.className = 'checkout-mode-menu-btn checkout-mode-menu-btn-primary';
|
|
271
296
|
applyBtn.textContent = 'Apply';
|
|
272
297
|
applyBtn.addEventListener('click', function() {
|
|
273
|
-
const
|
|
298
|
+
const types = checkboxes.filter(function(cb) { return cb.checked; }).map(function(cb) { return cb.value; });
|
|
299
|
+
if (types.length === 0) return; // require at least one selected type
|
|
274
300
|
_closeCheckoutModeMenu();
|
|
275
|
-
_applyCheckoutModeChange(projectName, 'live',
|
|
301
|
+
_applyCheckoutModeChange(projectName, 'live', types);
|
|
276
302
|
});
|
|
277
303
|
actions.appendChild(applyBtn);
|
|
278
304
|
|
|
@@ -284,12 +310,21 @@ function _renderCheckoutModeHybridStep(menu, projectName) {
|
|
|
284
310
|
// call (mirrors projectChipRemove / removePinnedNote), then POSTs to
|
|
285
311
|
// /api/settings. On failure, reverts the optimistic flip, re-renders, and
|
|
286
312
|
// shows an error toast.
|
|
313
|
+
//
|
|
314
|
+
// `liveValidationType` accepts a single type string (legacy call sites) OR an
|
|
315
|
+
// array of type strings (W-mr2m1ute000a9c01 — multi-select hybrid types);
|
|
316
|
+
// whichever shape is passed through is what gets POSTed as
|
|
317
|
+
// liveValidation.type, so shared.validateLiveValidation on the server decides
|
|
318
|
+
// the final persisted shape.
|
|
287
319
|
async function _applyCheckoutModeChange(projectName, newMode, liveValidationType) {
|
|
288
320
|
const project = _findProjectInLastStatus(projectName);
|
|
289
321
|
if (!project) return;
|
|
322
|
+
const typesForLabel = liveValidationType
|
|
323
|
+
? (Array.isArray(liveValidationType) ? liveValidationType : [liveValidationType])
|
|
324
|
+
: [];
|
|
290
325
|
const label = newMode === 'worktree'
|
|
291
326
|
? 'Worktrees'
|
|
292
|
-
: (
|
|
327
|
+
: (typesForLabel.length > 0 ? ('Hybrid (' + typesForLabel.join(', ') + ')') : 'Live checkout');
|
|
293
328
|
const ok = await confirmDialog({
|
|
294
329
|
title: 'Change checkout mode?',
|
|
295
330
|
message: 'Switch "' + projectName + '" to ' + label + '? This changes how every future dispatch on this project runs.',
|
package/dashboard/styles.css
CHANGED
|
@@ -1330,11 +1330,17 @@
|
|
|
1330
1330
|
font-size: var(--text-xs); color: var(--muted); font-weight: 400;
|
|
1331
1331
|
margin-top: 2px; line-height: 1.35;
|
|
1332
1332
|
}
|
|
1333
|
-
.checkout-mode-menu-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
border-radius: 4px;
|
|
1333
|
+
.checkout-mode-menu-checkboxes {
|
|
1334
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
1335
|
+
margin: 4px 0 8px; max-height: 220px; overflow-y: auto;
|
|
1337
1336
|
}
|
|
1337
|
+
.checkout-mode-menu-checkbox-row {
|
|
1338
|
+
display: flex; align-items: center; gap: 6px;
|
|
1339
|
+
padding: 4px 6px; border-radius: 4px; cursor: pointer;
|
|
1340
|
+
color: var(--text); font-size: var(--text-sm);
|
|
1341
|
+
}
|
|
1342
|
+
.checkout-mode-menu-checkbox-row:hover { background: var(--surface); }
|
|
1343
|
+
.checkout-mode-menu-checkbox { cursor: pointer; }
|
|
1338
1344
|
.checkout-mode-menu-actions {
|
|
1339
1345
|
display: flex; justify-content: flex-end; gap: 6px; padding-top: 4px;
|
|
1340
1346
|
}
|
package/dashboard.js
CHANGED
|
@@ -4300,7 +4300,10 @@ function _resetPreambleCache() {
|
|
|
4300
4300
|
function _projectCheckoutModeLabel(p) {
|
|
4301
4301
|
const mode = shared.resolveCheckoutMode(p);
|
|
4302
4302
|
if (mode === 'live' && p && p.liveValidation && p.liveValidation.type) {
|
|
4303
|
-
|
|
4303
|
+
// liveValidation.type may be a single string (legacy) or an array of
|
|
4304
|
+
// strings (W-mr2m1ute000a9c01 — multi-select hybrid types); render all.
|
|
4305
|
+
const types = Array.isArray(p.liveValidation.type) ? p.liveValidation.type : [p.liveValidation.type];
|
|
4306
|
+
return `hybrid (live-validation: ${types.join(', ')})`;
|
|
4304
4307
|
}
|
|
4305
4308
|
return mode;
|
|
4306
4309
|
}
|
package/engine/lifecycle.js
CHANGED
|
@@ -6503,9 +6503,20 @@ function collapseAllDuplicatePrRecords(config) {
|
|
|
6503
6503
|
|
|
6504
6504
|
// M003 — After a coding WI completes successfully, auto-dispatch a live-validation
|
|
6505
6505
|
// WI when project.liveValidation.autoDispatch === true and the completed item is
|
|
6506
|
-
// a coding WI (not the validation
|
|
6507
|
-
//
|
|
6508
|
-
//
|
|
6506
|
+
// a coding WI (not one of the validation types itself). Skips if the coding WI
|
|
6507
|
+
// has no PR.
|
|
6508
|
+
//
|
|
6509
|
+
// liveValidation.type may be a single string (legacy / back-compat) or an
|
|
6510
|
+
// array of strings (W-mr2m1ute000a9c01 — multi-select hybrid types). When an
|
|
6511
|
+
// array, ONE validation WI is dispatched per configured type (each validation
|
|
6512
|
+
// WI still carries a single canonical `type` field so downstream routing —
|
|
6513
|
+
// playbooks, agent resolution, resolveCheckoutMode's membership check — keeps
|
|
6514
|
+
// working per-type).
|
|
6515
|
+
//
|
|
6516
|
+
// Deduplicates per (codingWiId, validationType) pair: a non-terminal WI with
|
|
6517
|
+
// meta.liveValidationFor === codingWiId AND meta.liveValidationType === type
|
|
6518
|
+
// blocks a second dispatch of that specific type, so multiple configured
|
|
6519
|
+
// types don't collide or skip each other.
|
|
6509
6520
|
function autoDispatchLiveValidationWi(meta, config) {
|
|
6510
6521
|
const item = meta?.item;
|
|
6511
6522
|
if (!item?.id || !item?.type) return;
|
|
@@ -6521,8 +6532,12 @@ function autoDispatchLiveValidationWi(meta, config) {
|
|
|
6521
6532
|
const lv = project.liveValidation;
|
|
6522
6533
|
if (!lv || lv.autoDispatch !== true || !lv.type) return;
|
|
6523
6534
|
|
|
6524
|
-
|
|
6525
|
-
|
|
6535
|
+
const lvTypes = Array.isArray(lv.type) ? lv.type : [lv.type];
|
|
6536
|
+
|
|
6537
|
+
// Only auto-dispatch for coding WIs — skip types that ARE one of the
|
|
6538
|
+
// configured validation types (avoid an infinite validate-the-validation loop).
|
|
6539
|
+
const typesToDispatch = lvTypes.filter(t => t !== item.type);
|
|
6540
|
+
if (typesToDispatch.length === 0) return;
|
|
6526
6541
|
|
|
6527
6542
|
// Resolve PR reference: prefer the canonical stamped _pr field (set by
|
|
6528
6543
|
// stampWiPrRef which runs earlier in runPostCompletionHooks), then fall
|
|
@@ -6538,34 +6553,41 @@ function autoDispatchLiveValidationWi(meta, config) {
|
|
|
6538
6553
|
mutateWorkItems(wiPath, items => {
|
|
6539
6554
|
if (!Array.isArray(items)) return items;
|
|
6540
6555
|
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6556
|
+
for (const validationType of typesToDispatch) {
|
|
6557
|
+
// Dedup: skip if a non-terminal WI already tracks this (coding WI, type)
|
|
6558
|
+
// pair. Falls back to the WI's own `type` field when meta.liveValidationType
|
|
6559
|
+
// is absent (back-compat with validation WIs created before this field
|
|
6560
|
+
// existed, e.g. single-type configs from before W-mr2m1ute000a9c01).
|
|
6561
|
+
const existing = items.find(i =>
|
|
6562
|
+
i &&
|
|
6563
|
+
i.meta &&
|
|
6564
|
+
i.meta.liveValidationFor === codingWiId &&
|
|
6565
|
+
(i.meta.liveValidationType === validationType ||
|
|
6566
|
+
(!i.meta.liveValidationType && i.type === validationType)) &&
|
|
6567
|
+
!PLAN_TERMINAL_STATUSES.has(i.status)
|
|
6568
|
+
);
|
|
6569
|
+
if (existing) {
|
|
6570
|
+
log('info', `liveValidation: dedup — non-terminal validation WI ${existing.id} (type=${validationType}) already exists for ${codingWiId}`);
|
|
6571
|
+
continue;
|
|
6572
|
+
}
|
|
6552
6573
|
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6574
|
+
const validationWi = {
|
|
6575
|
+
id: 'W-' + shared.uid(),
|
|
6576
|
+
title: 'Validate: ' + (item.title || codingWiId),
|
|
6577
|
+
type: validationType,
|
|
6578
|
+
status: WI_STATUS.PENDING,
|
|
6579
|
+
depends_on: [codingWiId],
|
|
6580
|
+
references: [prRef],
|
|
6581
|
+
meta: { liveValidationFor: codingWiId, liveValidationType: validationType },
|
|
6582
|
+
project: projectName,
|
|
6583
|
+
priority: item.priority || 'medium',
|
|
6584
|
+
created: ts(),
|
|
6585
|
+
createdBy: 'lifecycle:live-validation-auto-dispatch',
|
|
6586
|
+
};
|
|
6566
6587
|
|
|
6567
|
-
|
|
6568
|
-
|
|
6588
|
+
items.push(validationWi);
|
|
6589
|
+
log('info', `liveValidation: auto-dispatched validation WI ${validationWi.id} (type=${validationType}) for coding WI ${codingWiId}`);
|
|
6590
|
+
}
|
|
6569
6591
|
return items;
|
|
6570
6592
|
});
|
|
6571
6593
|
} catch (err) {
|
package/engine/shared.js
CHANGED
|
@@ -2853,8 +2853,14 @@ function resolveCheckoutMode(project, workItemType) {
|
|
|
2853
2853
|
}
|
|
2854
2854
|
if (canonical === CHECKOUT_MODES.LIVE) {
|
|
2855
2855
|
// Apply liveValidation routing when the block is present and workItemType is provided.
|
|
2856
|
+
// liveValidation.type may be a single string (legacy / back-compat) or an
|
|
2857
|
+
// array of strings (W-mr2m1ute000a9c01, multi-select hybrid types) —
|
|
2858
|
+
// normalize to an array and do a membership check so either shape works.
|
|
2856
2859
|
if (project.liveValidation && workItemType !== undefined) {
|
|
2857
|
-
|
|
2860
|
+
const lvTypes = Array.isArray(project.liveValidation.type)
|
|
2861
|
+
? project.liveValidation.type
|
|
2862
|
+
: [project.liveValidation.type];
|
|
2863
|
+
return lvTypes.includes(workItemType)
|
|
2858
2864
|
? CHECKOUT_MODES.LIVE
|
|
2859
2865
|
: CHECKOUT_MODES.WORKTREE;
|
|
2860
2866
|
}
|
|
@@ -2910,13 +2916,26 @@ function validateCheckoutMode(value) {
|
|
|
2910
2916
|
// Validate + normalize a per-project `liveValidation` block (hybrid mode).
|
|
2911
2917
|
// Hybrid = checkoutMode:'live' + liveValidation:{ type, autoDispatch }: coding
|
|
2912
2918
|
// work items author in isolated worktrees (escaping the live cap) while only
|
|
2913
|
-
// work items whose type
|
|
2919
|
+
// work items whose type is IN liveValidation.type run in-place on the live
|
|
2914
2920
|
// checkout. See resolveCheckoutMode (which routes per work-item type) and
|
|
2915
2921
|
// docs/live-checkout-mode.md.
|
|
2916
2922
|
//
|
|
2923
|
+
// `type` may be a single work-item-type string (legacy / back-compat) OR a
|
|
2924
|
+
// non-empty array of non-empty-string work-item types
|
|
2925
|
+
// (W-mr2m1ute000a9c01 — multi-select hybrid types, e.g. both "implement" and
|
|
2926
|
+
// "fix" validate live while other types stay in worktrees). Every reader of
|
|
2927
|
+
// liveValidation.type (resolveCheckoutMode, lifecycle.autoDispatchLiveValidationWi,
|
|
2928
|
+
// dashboard project mapper / pill / CC label) must handle BOTH shapes —
|
|
2929
|
+
// existing config.json entries with a plain string `type` continue to work
|
|
2930
|
+
// unchanged, no migration required.
|
|
2931
|
+
//
|
|
2917
2932
|
// Returns:
|
|
2918
2933
|
// - undefined → caller should clear the field (empty / null / '' input)
|
|
2919
|
-
// - { type, autoDispatch? } normalized object on success
|
|
2934
|
+
// - { type, autoDispatch? } normalized object on success, where `type` is
|
|
2935
|
+
// whatever shape the caller passed in (string stays a string; array stays
|
|
2936
|
+
// a deduped array) — we intentionally do NOT force single-string input
|
|
2937
|
+
// into an array so already-persisted single-type configs round-trip
|
|
2938
|
+
// byte-identical through a Settings save that doesn't touch this field.
|
|
2920
2939
|
// Throws HTTP 400 (via _httpError) on malformed input, or when the effective
|
|
2921
2940
|
// checkout mode is not 'live' (liveValidation is meaningless without it — it is
|
|
2922
2941
|
// silently ignored at resolve time, so we refuse to persist a misconfiguration).
|
|
@@ -2933,15 +2952,33 @@ function validateLiveValidation(value, opts) {
|
|
|
2933
2952
|
if (checkoutMode !== CHECKOUT_MODES.LIVE) {
|
|
2934
2953
|
throw _httpError(400, 'liveValidation requires checkoutMode "live" (hybrid mode). Set checkoutMode to "live" first, or clear liveValidation.');
|
|
2935
2954
|
}
|
|
2936
|
-
const
|
|
2937
|
-
|
|
2938
|
-
|
|
2955
|
+
const typeErrorMsg = 'Invalid liveValidation: "type" is required and must be a non-empty string, or a non-empty array of non-empty-string work-item types — the work-item type(s) that run on the live checkout (e.g. "build-and-test" or ["implement", "fix"]).';
|
|
2956
|
+
const rawType = value.type;
|
|
2957
|
+
let out;
|
|
2958
|
+
if (typeof rawType === 'string') {
|
|
2959
|
+
const trimmed = rawType.trim();
|
|
2960
|
+
if (!trimmed) throw _httpError(400, typeErrorMsg);
|
|
2961
|
+
out = { type: trimmed };
|
|
2962
|
+
} else if (Array.isArray(rawType)) {
|
|
2963
|
+
if (!rawType.every(t => typeof t === 'string' && t.trim().length > 0)) {
|
|
2964
|
+
throw _httpError(400, typeErrorMsg);
|
|
2965
|
+
}
|
|
2966
|
+
// Dedupe (preserving first-seen order) and cap at the number of distinct
|
|
2967
|
+
// work-item types the engine recognizes — a longer array can only be
|
|
2968
|
+
// full of duplicates or invented types.
|
|
2969
|
+
const deduped = [...new Set(rawType.map(t => t.trim()))];
|
|
2970
|
+
if (deduped.length === 0) throw _httpError(400, typeErrorMsg);
|
|
2971
|
+
if (deduped.length > VALID_WORK_TYPES.size) {
|
|
2972
|
+
throw _httpError(400, `Invalid liveValidation: "type" array has ${deduped.length} entries, more than the ${VALID_WORK_TYPES.size} distinct work-item types the engine recognizes — remove duplicates or invalid types.`);
|
|
2973
|
+
}
|
|
2974
|
+
out = { type: deduped };
|
|
2975
|
+
} else {
|
|
2976
|
+
throw _httpError(400, typeErrorMsg);
|
|
2939
2977
|
}
|
|
2940
|
-
const out = { type };
|
|
2941
2978
|
// autoDispatch (optional): when true, lifecycle auto-creates a validation WI
|
|
2942
|
-
//
|
|
2943
|
-
// autoDispatchLiveValidationWi). Coerce to a strict
|
|
2944
|
-
// off so the operator opts in explicitly.
|
|
2979
|
+
// per configured `type` after each coding WI completes with a PR
|
|
2980
|
+
// (engine/lifecycle.js autoDispatchLiveValidationWi). Coerce to a strict
|
|
2981
|
+
// boolean; absent leaves it off so the operator opts in explicitly.
|
|
2945
2982
|
if (Object.prototype.hasOwnProperty.call(value, 'autoDispatch')) {
|
|
2946
2983
|
out.autoDispatch = !!value.autoDispatch;
|
|
2947
2984
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2310",
|
|
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"
|
package/prompts/cc-system.md
CHANGED
|
@@ -325,7 +325,7 @@ Every configured project has an effective **checkout mode** — surfaced in your
|
|
|
325
325
|
|
|
326
326
|
- **`worktree`** (default) — each dispatch gets its own isolated `git worktree`. Agents run fully in parallel; nothing touches the operator's working tree. Use for normal repos.
|
|
327
327
|
- **`live`** — agents run **in-place** inside the project's `localPath` (no worktree). The engine caps this to **one mutating dispatch at a time** per project and **refuses on a dirty tree**. Use only when worktrees are unworkable (e.g. Android `repo`, submodules, deep Windows paths, emulators that bind the real checkout).
|
|
328
|
-
- **`hybrid`** — `live` **plus** a `liveValidation: { type, autoDispatch }` block. Coding work items (`implement`/`fix`/`docs`/`decompose`) author in **isolated worktrees** (full parallelism), while **only** work items whose type matches `liveValidation.type` (e.g. `build-and-test`) run **in-place on the live checkout**. This is the best of both: parallel code authoring + a real on-disk build/validation that can't run in a worktree.
|
|
328
|
+
- **`hybrid`** — `live` **plus** a `liveValidation: { type, autoDispatch }` block. Coding work items (`implement`/`fix`/`docs`/`decompose`) author in **isolated worktrees** (full parallelism), while **only** work items whose type matches `liveValidation.type` (e.g. `build-and-test`) run **in-place on the live checkout**. This is the best of both: parallel code authoring + a real on-disk build/validation that can't run in a worktree. `type` accepts a single string (e.g. `"build-and-test"`) or an array of strings (e.g. `["implement","fix"]`) to route multiple work-item types to the live checkout at once.
|
|
329
329
|
|
|
330
330
|
**When to recommend hybrid:** the project's build/test/validation genuinely cannot run in an isolated worktree (it needs the real checkout — submodules, a `repo`-managed tree, an emulator/dev-server bound to `localPath`, deep-path tooling), **but** you still want coding agents to work in parallel rather than serialize through the single live checkout. If the *whole* workflow must run on the real tree, use plain `live`. If nothing needs the real tree, stay on `worktree`.
|
|
331
331
|
|