@yemi33/minions 0.1.1917 → 0.1.1919
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/render-work-items.js +10 -1
- package/dashboard.js +14 -0
- package/engine/features.js +17 -0
- package/engine/lifecycle.js +35 -0
- package/package.json +1 -1
- package/engine/copilot-models.json +0 -5
|
@@ -161,6 +161,9 @@ function editWorkItem(id, source) {
|
|
|
161
161
|
'<label style="color:var(--text);font-size:var(--text-md)">Acceptance Criteria (one per line)' +
|
|
162
162
|
'<textarea id="wi-edit-ac" rows="3" style="display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:var(--text-md);font-family:inherit;resize:vertical">' + escapeHtml((item.acceptanceCriteria || []).join('\n')) + '</textarea>' +
|
|
163
163
|
'</label>' +
|
|
164
|
+
'<label style="color:var(--text);font-size:var(--text-md)">Depends On (work-item ids, comma- or newline-separated)' +
|
|
165
|
+
'<textarea id="wi-edit-depends-on" rows="2" placeholder="W-foo, W-bar" style="display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:var(--text-md);font-family:inherit;resize:vertical">' + escapeHtml((Array.isArray(item.depends_on) ? item.depends_on : []).join(', ')) + '</textarea>' +
|
|
166
|
+
'</label>' +
|
|
164
167
|
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:8px">' +
|
|
165
168
|
'<button onclick="closeModal()" class="pr-pager-btn" style="padding:6px 16px;font-size:var(--text-md)">Cancel</button>' +
|
|
166
169
|
'<button onclick="submitWorkItemEdit(\'' + escapeHtml(id) + '\',\'' + escapeHtml(source || '') + '\',event)" style="padding:6px 16px;font-size:var(--text-md);background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Save</button>' +
|
|
@@ -183,13 +186,15 @@ async function submitWorkItemEdit(id, source, e) {
|
|
|
183
186
|
}).filter(function(r) { return r.url; });
|
|
184
187
|
const acRaw = document.getElementById('wi-edit-ac')?.value || '';
|
|
185
188
|
const acceptanceCriteria = acRaw.split('\n').filter(function(l) { return l.trim(); });
|
|
189
|
+
const dependsRaw = document.getElementById('wi-edit-depends-on')?.value || '';
|
|
190
|
+
const depends_on = dependsRaw.split(/[\n,]/).map(function(s) { return s.trim(); }).filter(Boolean);
|
|
186
191
|
if (!title) { if (btn) { btn.disabled = false; btn.textContent = 'Save'; } alert('Title is required'); return; }
|
|
187
192
|
try { closeModal(); } catch { /* may not be open */ }
|
|
188
193
|
showToast('cmd-toast', 'Work item updated', true);
|
|
189
194
|
try {
|
|
190
195
|
const res = await fetch('/api/work-items/update', {
|
|
191
196
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
192
|
-
body: JSON.stringify({ id, source: source || undefined, title, description, type, priority, agent, references, acceptanceCriteria })
|
|
197
|
+
body: JSON.stringify({ id, source: source || undefined, title, description, type, priority, agent, references, acceptanceCriteria, depends_on })
|
|
193
198
|
});
|
|
194
199
|
if (res.ok) { refresh(); } else { const d = await res.json().catch(() => ({})); alert('Update failed: ' + (d.error || 'unknown')); editWorkItem(id, source); }
|
|
195
200
|
} catch (e) { alert('Update error: ' + e.message); editWorkItem(id, source); }
|
|
@@ -375,6 +380,7 @@ function openCreateWorkItemModal() {
|
|
|
375
380
|
'<label style="flex:1;color:var(--text);font-size:var(--text-md)">Project <select id="wi-new-project" style="' + inputStyle + '"><option value="">Central</option>' + projOpts + '</select></label>' +
|
|
376
381
|
'</div>' +
|
|
377
382
|
'<label style="color:var(--text);font-size:var(--text-md)">Acceptance Criteria <textarea id="wi-new-ac" rows="2" style="' + inputStyle + ';resize:vertical" placeholder="One criterion per line (optional)"></textarea></label>' +
|
|
383
|
+
'<label style="color:var(--text);font-size:var(--text-md)">Depends On <textarea id="wi-new-depends-on" rows="2" style="' + inputStyle + ';resize:vertical" placeholder="W-foo, W-bar — comma- or newline-separated work-item ids (optional)"></textarea></label>' +
|
|
378
384
|
'<label style="color:var(--text);font-size:var(--text-md)">References <textarea id="wi-new-refs" rows="2" style="' + inputStyle + ';resize:vertical" placeholder="url | title | type — one per line (optional)"></textarea></label>' +
|
|
379
385
|
'<label id="wi-new-skippr-row" style="color:var(--text);font-size:var(--text-md);display:flex;gap:8px;align-items:center;cursor:pointer"><input type="checkbox" id="wi-new-skippr"> Skip PR creation (push branch only)</label>' +
|
|
380
386
|
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:4px">' +
|
|
@@ -406,6 +412,8 @@ async function _submitCreateWorkItem(e) {
|
|
|
406
412
|
const project = document.getElementById('wi-new-project')?.value || '';
|
|
407
413
|
const acRaw = document.getElementById('wi-new-ac')?.value || '';
|
|
408
414
|
const acceptanceCriteria = acRaw.split('\n').map(l => l.trim()).filter(Boolean);
|
|
415
|
+
const dependsRaw = document.getElementById('wi-new-depends-on')?.value || '';
|
|
416
|
+
const depends_on = dependsRaw.split(/[\n,]/).map(s => s.trim()).filter(Boolean);
|
|
409
417
|
const refsRaw = document.getElementById('wi-new-refs')?.value || '';
|
|
410
418
|
const references = refsRaw.split('\n').filter(l => l.trim()).map(l => {
|
|
411
419
|
const parts = l.split('|').map(s => s.trim());
|
|
@@ -418,6 +426,7 @@ async function _submitCreateWorkItem(e) {
|
|
|
418
426
|
if (project) body.project = project;
|
|
419
427
|
if (acceptanceCriteria.length) body.acceptanceCriteria = acceptanceCriteria;
|
|
420
428
|
if (references.length && references[0].url) body.references = references;
|
|
429
|
+
if (depends_on.length) body.depends_on = depends_on;
|
|
421
430
|
const skipPr = document.getElementById('wi-new-skippr')?.checked || false;
|
|
422
431
|
if (skipPr) body.skipPr = true;
|
|
423
432
|
|
package/dashboard.js
CHANGED
|
@@ -3779,6 +3779,10 @@ const server = http.createServer(async (req, res) => {
|
|
|
3779
3779
|
try {
|
|
3780
3780
|
const body = await readBody(req);
|
|
3781
3781
|
if (!body.title || !body.title.trim()) return jsonReply(res, 400, { error: 'title is required' });
|
|
3782
|
+
if (body.depends_on !== undefined) {
|
|
3783
|
+
if (!Array.isArray(body.depends_on)) return jsonReply(res, 400, { error: 'depends_on must be an array of strings' });
|
|
3784
|
+
if (!body.depends_on.every(s => typeof s === 'string')) return jsonReply(res, 400, { error: 'depends_on entries must be strings' });
|
|
3785
|
+
}
|
|
3782
3786
|
const target = resolveWorkItemsCreateTarget(body.project);
|
|
3783
3787
|
if (target.error) return jsonReply(res, 400, { error: target.error });
|
|
3784
3788
|
const wiPath = target.wiPath;
|
|
@@ -3789,6 +3793,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
3789
3793
|
priority: body.priority || 'medium', description: body.description || '',
|
|
3790
3794
|
status: WI_STATUS.PENDING, created: new Date().toISOString(), createdBy: 'dashboard',
|
|
3791
3795
|
};
|
|
3796
|
+
if (Array.isArray(body.depends_on)) {
|
|
3797
|
+
item.depends_on = body.depends_on.map(s => s.trim()).filter(Boolean);
|
|
3798
|
+
}
|
|
3792
3799
|
if (targetProject) item.project = targetProject.name;
|
|
3793
3800
|
if (body.scope) item.scope = body.scope;
|
|
3794
3801
|
// Agent assignment normalization: `agent` and `agents` are routing hints.
|
|
@@ -3832,6 +3839,10 @@ const server = http.createServer(async (req, res) => {
|
|
|
3832
3839
|
const body = await readBody(req);
|
|
3833
3840
|
const { id, source, title, description, type, priority, agent } = body;
|
|
3834
3841
|
if (!id) return jsonReply(res, 400, { error: 'id required' });
|
|
3842
|
+
if (body.depends_on !== undefined) {
|
|
3843
|
+
if (!Array.isArray(body.depends_on)) return jsonReply(res, 400, { error: 'depends_on must be an array of strings' });
|
|
3844
|
+
if (!body.depends_on.every(s => typeof s === 'string')) return jsonReply(res, 400, { error: 'depends_on entries must be strings' });
|
|
3845
|
+
}
|
|
3835
3846
|
|
|
3836
3847
|
const target = resolveProjectSourceTarget(source, PROJECTS);
|
|
3837
3848
|
if (target.error) return jsonReply(res, 404, { error: target.error });
|
|
@@ -3855,6 +3866,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
3855
3866
|
}
|
|
3856
3867
|
if (body.references !== undefined) item.references = body.references;
|
|
3857
3868
|
if (body.acceptanceCriteria !== undefined) item.acceptanceCriteria = body.acceptanceCriteria;
|
|
3869
|
+
if (Array.isArray(body.depends_on)) {
|
|
3870
|
+
item.depends_on = body.depends_on.map(s => s.trim()).filter(Boolean);
|
|
3871
|
+
}
|
|
3858
3872
|
item.updatedAt = new Date().toISOString();
|
|
3859
3873
|
result = { code: 200, body: { ok: true, item } };
|
|
3860
3874
|
return items;
|
package/engine/features.js
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
// Entry shape: id → { description, default: bool, addedIn?: version, expires?: ISO-date }
|
|
6
|
+
//
|
|
7
|
+
// INTENTIONAL EMPTY REGISTRY — DO NOT DELETE.
|
|
8
|
+
//
|
|
9
|
+
// The `FEATURES = {}` literal below is intentional scaffolding, not dead code.
|
|
10
|
+
// The surrounding framework (isFeatureOn, listFeatures, hasFeature, env-var
|
|
11
|
+
// override resolver, expiration timestamps) is load-bearing: dashboard.js
|
|
12
|
+
// boot wires it into /api/features (list), /api/features/toggle, and the
|
|
13
|
+
// `window.MINIONS_FEATURES` client bootstrap. Deleting the registry — even
|
|
14
|
+
// while empty — would break dashboard startup and the experimental-flags UI.
|
|
15
|
+
//
|
|
16
|
+
// New flags belong here. Register them in this object instead of removing
|
|
17
|
+
// the empty literal. The first real entry will replace the example below;
|
|
18
|
+
// until then the empty object is the correct, expected shape.
|
|
19
|
+
//
|
|
20
|
+
// Decision logged in the 2026-05-13 daily architecture & bug review meeting
|
|
21
|
+
// (knowledge/architecture/2026-05-13-ripley-meeting-conclusion-daily-architecture-bug-review-2.md,
|
|
22
|
+
// PR-C, Option B — keep framework, document the empty registry).
|
|
6
23
|
const FEATURES = {
|
|
7
24
|
// Example:
|
|
8
25
|
// 'ux-sidebar-v2': { description: '…', default: false, addedIn: '0.1.1738', expires: '2026-06-01' },
|
package/engine/lifecycle.js
CHANGED
|
@@ -2597,6 +2597,12 @@ function parseAgentOutput(stdout, runtimeName) {
|
|
|
2597
2597
|
* Agents produce a ```completion fenced block with key: value pairs.
|
|
2598
2598
|
* Returns parsed object or null if not found / malformed.
|
|
2599
2599
|
* If multiple blocks exist, the last one wins (agent may retry).
|
|
2600
|
+
*
|
|
2601
|
+
* DEPRECATED — slated for removal once telemetry shows zero hits over a 14-day
|
|
2602
|
+
* window. Telemetry: see _engine.completionFallbacks in metrics.json (the JSON
|
|
2603
|
+
* sidecar at MINIONS_COMPLETION_REPORT is the documented contract; this fenced
|
|
2604
|
+
* fallback exists only to support runtimes/agents that have not yet adopted it).
|
|
2605
|
+
* Removal is tracked by the P-c8f5e1b3 follow-up plan.
|
|
2600
2606
|
*/
|
|
2601
2607
|
function parseStructuredCompletion(stdout, runtimeName) {
|
|
2602
2608
|
if (!stdout || typeof stdout !== 'string') return null;
|
|
@@ -2692,6 +2698,13 @@ function parseCompletionKeyValues(text) {
|
|
|
2692
2698
|
return result;
|
|
2693
2699
|
}
|
|
2694
2700
|
|
|
2701
|
+
/**
|
|
2702
|
+
* DEPRECATED — slated for removal once telemetry shows zero hits over a 14-day
|
|
2703
|
+
* window. Telemetry: see _engine.completionFallbacks in metrics.json (the JSON
|
|
2704
|
+
* sidecar at MINIONS_COMPLETION_REPORT is the documented contract; this prose
|
|
2705
|
+
* fallback only fires when both the sidecar AND the fenced ```completion block
|
|
2706
|
+
* are missing). Removal is tracked by the P-c8f5e1b3 follow-up plan.
|
|
2707
|
+
*/
|
|
2695
2708
|
function parseCompletionFieldSummary(text) {
|
|
2696
2709
|
if (!text || typeof text !== 'string') return null;
|
|
2697
2710
|
|
|
@@ -3203,6 +3216,28 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
|
|
|
3203
3216
|
const summaryCompletion = reportCompletion || fencedCompletion ? null : parseCompletionFieldSummary(resultSummary);
|
|
3204
3217
|
const fallbackCompletion = fencedCompletion || summaryCompletion;
|
|
3205
3218
|
const fallbackSource = fencedCompletion && hasCompletionFence(stdout, runtimeName) ? 'fenced-completion' : 'summary-completion';
|
|
3219
|
+
// P-c8f5e1b3 — telemetry: completion-fallback. Emit a grep-able log + bump a
|
|
3220
|
+
// counter in metrics.json whenever the fenced or summary fallback fires (i.e.
|
|
3221
|
+
// the documented sidecar contract was missing). Used to confirm zero adoption
|
|
3222
|
+
// gaps before removing parseStructuredCompletion / parseCompletionFieldSummary
|
|
3223
|
+
// in a follow-up. Sidecar (happy) path is silent — no log, no increment.
|
|
3224
|
+
if (!reportCompletion && fallbackCompletion) {
|
|
3225
|
+
const counterKey = fallbackSource === 'fenced-completion' ? 'fenced' : 'summary';
|
|
3226
|
+
const wiId = dispatchItem.meta?.item?.id || null;
|
|
3227
|
+
log('info', `telemetry: completion-fallback agent=${agentId || 'unknown'} wi=${wiId || 'N/A'} runtime=${runtimeName} source=${fallbackSource}`);
|
|
3228
|
+
try {
|
|
3229
|
+
const metricsPath = path.join(ENGINE_DIR, 'metrics.json');
|
|
3230
|
+
mutateJsonFileLocked(metricsPath, (metrics) => {
|
|
3231
|
+
metrics = metrics || {};
|
|
3232
|
+
if (!metrics._engine) metrics._engine = {};
|
|
3233
|
+
if (!metrics._engine.completionFallbacks) metrics._engine.completionFallbacks = { fenced: 0, summary: 0 };
|
|
3234
|
+
metrics._engine.completionFallbacks[counterKey] = (metrics._engine.completionFallbacks[counterKey] || 0) + 1;
|
|
3235
|
+
return metrics;
|
|
3236
|
+
});
|
|
3237
|
+
} catch (err) {
|
|
3238
|
+
log('warn', `telemetry: completion-fallback metrics write failed: ${err.message}`);
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3206
3241
|
const structuredCompletion = reportCompletion || persistCompletionReport(dispatchItem, fallbackCompletion, fallbackSource);
|
|
3207
3242
|
if (structuredCompletion) {
|
|
3208
3243
|
if (structuredCompletion.summary) resultSummary = String(structuredCompletion.summary);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1919",
|
|
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"
|