@yemi33/minions 0.1.2150 → 0.1.2152
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/bin/minions.js +99 -6
- package/dashboard/docs/typography-audit.md +128 -0
- package/dashboard/docs/typography.md +114 -0
- package/dashboard/js/render-agents.js +1 -1
- package/dashboard/js/utils.js +1 -1
- package/dashboard/slim/body.html +2 -2
- package/dashboard/slim/styles.css +112 -77
- package/dashboard/styles.css +37 -3
- package/engine/cli.js +5 -3
- package/engine/dispatch.js +26 -1
- package/engine/lifecycle.js +89 -0
- package/package.json +6 -1
package/dashboard/styles.css
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[data-font-size] on <html>. 'small' is the historic default (1.0) so
|
|
4
4
|
existing users see no change. Body-level CSS `zoom` is the cheapest
|
|
5
5
|
way to scale every px/em/rem rule across the SPA (typography tokens
|
|
6
|
-
below, slim.html, and the many inline
|
|
6
|
+
below, slim.html, and the many inline font-size Npx styles) without
|
|
7
7
|
rewriting every rule. Scales modals, drawers, and fixed-position
|
|
8
8
|
elements because they all live inside <body>. */
|
|
9
9
|
--minions-font-scale: 1;
|
|
@@ -15,11 +15,30 @@
|
|
|
15
15
|
--space-1: 2px; --space-2: 4px; --space-3: 6px; --space-4: 8px;
|
|
16
16
|
--space-5: 10px; --space-6: 12px; --space-7: 16px; --space-8: 20px; --space-9: 24px;
|
|
17
17
|
|
|
18
|
-
/* Typography
|
|
18
|
+
/* Typography — size primitives (raw px). Source of truth for the
|
|
19
|
+
~700 callsites migrated in PR #66; do NOT introduce new raw `Npx`
|
|
20
|
+
font-sizes outside this block. Tripwire:
|
|
21
|
+
test/unit/dashboard-font-size-tokens.test.js */
|
|
19
22
|
--text-xs: 10px; --text-sm: 11px; --text-base: 12px;
|
|
20
23
|
--text-md: 13px; --text-lg: 14px; --text-xl: 16px; --text-2xl: 18px;
|
|
21
24
|
--text-stat: 22px; --text-stat-lg: 28px; --text-display: 32px;
|
|
22
25
|
|
|
26
|
+
/* Typography — role aliases (W-mq1c8og40003a7cf). Prefer these for
|
|
27
|
+
NEW code: the role names survive a size-scale redesign while the
|
|
28
|
+
primitives do not. Existing callsites that still reference the
|
|
29
|
+
size primitives directly are kept (they're correct by
|
|
30
|
+
construction — every role alias is just an indirection). See
|
|
31
|
+
dashboard/docs/typography.md for the role → primitive map and
|
|
32
|
+
intended use. */
|
|
33
|
+
--text-role-display: var(--text-display); /* 32px — hero / page title */
|
|
34
|
+
--text-heading: var(--text-2xl); /* 18px — section + modal headers */
|
|
35
|
+
--text-subheading: var(--text-xl); /* 16px — secondary headers, card titles */
|
|
36
|
+
--text-body: var(--text-lg); /* 14px — default body text */
|
|
37
|
+
--text-meta: var(--text-md); /* 13px — captions, timestamps, secondary metadata */
|
|
38
|
+
--text-caption: var(--text-base); /* 12px — small labels, table cells */
|
|
39
|
+
--text-micro: var(--text-sm); /* 11px — chip / tag pill labels */
|
|
40
|
+
--text-code: 0.9em; /* inline code; relative so it scales with the surrounding text */
|
|
41
|
+
|
|
23
42
|
/* Border radius */
|
|
24
43
|
--radius-sm: 4px; --radius-md: 6px; --radius-lg: 8px; --radius-xl: 10px; --radius-full: 50%;
|
|
25
44
|
|
|
@@ -43,6 +62,21 @@
|
|
|
43
62
|
html, body { height: 100%; margin: 0; overflow: hidden; }
|
|
44
63
|
body { background: var(--bg); color: var(--text); font-family: 'Segoe UI', system-ui, sans-serif; font-size: var(--text-xl); display: flex; flex-direction: column; zoom: var(--minions-font-scale, 1); }
|
|
45
64
|
|
|
65
|
+
/* Typography utility classes (W-mq1c8og40003a7cf).
|
|
66
|
+
One class per role token. Apply directly on any element
|
|
67
|
+
(`<span class="text-meta">`) so JS/HTML callsites don't need to
|
|
68
|
+
hand-roll inline `style="font-size:var(--text-*)"`. The classes
|
|
69
|
+
ONLY set font-size — weight, color, line-height stay with the
|
|
70
|
+
caller. Mirrors the `.btn-add` consolidation in spirit. */
|
|
71
|
+
.text-display { font-size: var(--text-role-display); }
|
|
72
|
+
.text-heading { font-size: var(--text-heading); }
|
|
73
|
+
.text-subheading { font-size: var(--text-subheading); }
|
|
74
|
+
.text-body { font-size: var(--text-body); }
|
|
75
|
+
.text-meta { font-size: var(--text-meta); }
|
|
76
|
+
.text-caption { font-size: var(--text-caption); }
|
|
77
|
+
.text-micro { font-size: var(--text-micro); }
|
|
78
|
+
.text-code { font-size: var(--text-code); }
|
|
79
|
+
|
|
46
80
|
header {
|
|
47
81
|
background: var(--surface); border-bottom: 1px solid var(--border);
|
|
48
82
|
padding: var(--space-6) 14px; display: flex; align-items: center; justify-content: space-between;
|
|
@@ -240,7 +274,7 @@
|
|
|
240
274
|
.prd-item-row.st-needs-human-review { border-left-color: var(--orange); }
|
|
241
275
|
.prd-item-row.st-updated { border-left-color: var(--purple); }
|
|
242
276
|
.prd-item-row.st-paused { border-left-color: var(--muted); opacity: 0.5; }
|
|
243
|
-
.prd-item-id { font-family: Consolas, monospace; color: var(--muted); min-width: 36px; font-size:
|
|
277
|
+
.prd-item-id { font-family: Consolas, monospace; color: var(--muted); min-width: 36px; font-size: var(--text-code); }
|
|
244
278
|
.prd-item-name { flex: 1; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
245
279
|
.prd-item-priority { font-size: var(--text-sm); padding: var(--space-1) var(--space-3); border-radius: var(--radius-lg); }
|
|
246
280
|
.prd-item-priority.high { background: rgba(248,81,73,0.15); color: var(--red); }
|
package/engine/cli.js
CHANGED
|
@@ -1304,15 +1304,17 @@ const commands = {
|
|
|
1304
1304
|
console.log(`Engine: ${control.state} (PID ${control.pid || 'N/A'})`);
|
|
1305
1305
|
}
|
|
1306
1306
|
|
|
1307
|
-
// Dashboard check
|
|
1307
|
+
// Dashboard check. Honors MINIONS_PORT so `minions --dev status` probes the
|
|
1308
|
+
// dev dashboard (7332 by default) rather than the binary install's 7331.
|
|
1308
1309
|
const http = require('http');
|
|
1310
|
+
const dashPort = Number(process.env.MINIONS_PORT) || 7331;
|
|
1309
1311
|
const dashCheck = new Promise(resolve => {
|
|
1310
|
-
const req = http.get(
|
|
1312
|
+
const req = http.get(`http://localhost:${dashPort}/api/health`, { timeout: 2000 }, () => resolve(true));
|
|
1311
1313
|
req.on('error', () => resolve(false));
|
|
1312
1314
|
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
1313
1315
|
});
|
|
1314
1316
|
dashCheck.then(dashUp => {
|
|
1315
|
-
if (dashUp) console.log(
|
|
1317
|
+
if (dashUp) console.log(`Dashboard: running (http://localhost:${dashPort})`);
|
|
1316
1318
|
else console.log('Dashboard: not running — start with: minions dash');
|
|
1317
1319
|
}).catch(() => {});
|
|
1318
1320
|
|
package/engine/dispatch.js
CHANGED
|
@@ -18,6 +18,18 @@ const { getConfig, INBOX_DIR } = queries;
|
|
|
18
18
|
|
|
19
19
|
const MINIONS_DIR = shared.MINIONS_DIR;
|
|
20
20
|
|
|
21
|
+
// Dispatch types that do not push code to a PR branch. These remain dispatchable
|
|
22
|
+
// against `_contextOnly: true` PRs because casting a review vote, posting a
|
|
23
|
+
// comment, asking a question, or running a read-only explore never mutates the
|
|
24
|
+
// PR's source branch. Auto-discovery (engine.js#discoverFromPrs) is still
|
|
25
|
+
// gated on `_contextOnly` separately — only explicit WIs (dashboard, watches,
|
|
26
|
+
// CC) can target a context-only PR, and only via these types.
|
|
27
|
+
const NON_MUTATING_DISPATCH_TYPES = new Set([
|
|
28
|
+
WORK_TYPE.REVIEW,
|
|
29
|
+
WORK_TYPE.ASK,
|
|
30
|
+
WORK_TYPE.EXPLORE,
|
|
31
|
+
]);
|
|
32
|
+
|
|
21
33
|
// Lazy require to break circular dependency with engine.js
|
|
22
34
|
let _lifecycle = null;
|
|
23
35
|
function lifecycle() { if (!_lifecycle) _lifecycle = require('./lifecycle'); return _lifecycle; }
|
|
@@ -358,7 +370,19 @@ function getStalePrDispatchReason(entry, config) {
|
|
|
358
370
|
const prLabel = entry.meta.pr?.id || entry.meta.pr?.url || entry.id;
|
|
359
371
|
if (!tracked) return `PR ${prLabel} is no longer tracked`;
|
|
360
372
|
if (tracked.status !== PR_STATUS.ACTIVE) return `PR ${tracked.id || prLabel} is ${tracked.status || 'missing status'}`;
|
|
361
|
-
|
|
373
|
+
// Combined gate (W-mq5v612m001g1545 + #3126):
|
|
374
|
+
// 1. `entry.meta?.source !== 'work-item'` — blocks any non-work-item dispatch
|
|
375
|
+
// (e.g. legacy `pr`/`pr-human-feedback` discovery paths) from running
|
|
376
|
+
// against a context-only PR. Auto-discovery in engine.js#discoverFromPrs
|
|
377
|
+
// already skips _contextOnly PRs at the source, so this is defense-in-depth.
|
|
378
|
+
// 2. `!NON_MUTATING_DISPATCH_TYPES.has(entry.type)` — even an explicit
|
|
379
|
+
// work-item dispatch is dropped if it's a mutating type (fix, implement,
|
|
380
|
+
// test, verify, decompose, docs). Only review/ask/explore — which never
|
|
381
|
+
// push to the PR's source branch — are allowed through.
|
|
382
|
+
if (tracked._contextOnly
|
|
383
|
+
&& (entry.meta?.source !== 'work-item' || !NON_MUTATING_DISPATCH_TYPES.has(entry.type))) {
|
|
384
|
+
return `PR ${tracked.id || prLabel} is context-only`;
|
|
385
|
+
}
|
|
362
386
|
|
|
363
387
|
const queuedBranch = entry.meta.branch || entry.meta.pr?.branch || '';
|
|
364
388
|
const trackedBranch = tracked.branch || '';
|
|
@@ -1048,4 +1072,5 @@ module.exports = {
|
|
|
1048
1072
|
findActivePrOrBranchLock,
|
|
1049
1073
|
normalizeRetryableDecision,
|
|
1050
1074
|
isCompletedWorkItemForFailure,
|
|
1075
|
+
NON_MUTATING_DISPATCH_TYPES,
|
|
1051
1076
|
};
|
package/engine/lifecycle.js
CHANGED
|
@@ -546,6 +546,64 @@ function resolveWorkItemPath(meta) {
|
|
|
546
546
|
return null;
|
|
547
547
|
}
|
|
548
548
|
|
|
549
|
+
function getWorkItemPaths(config) {
|
|
550
|
+
const paths = [path.join(MINIONS_DIR, 'work-items.json')];
|
|
551
|
+
for (const p of shared.getProjects(config || {})) {
|
|
552
|
+
if (p && p.name) paths.push(shared.projectWorkItemsPath(p));
|
|
553
|
+
}
|
|
554
|
+
return paths;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function findWorkItemRecord(itemId, config) {
|
|
558
|
+
if (!itemId) return null;
|
|
559
|
+
for (const wiPath of getWorkItemPaths(config)) {
|
|
560
|
+
try {
|
|
561
|
+
const items = readOptionalJsonStrict(wiPath, 'work-items', Array.isArray) || [];
|
|
562
|
+
const item = items.find(i => i && i.id === itemId);
|
|
563
|
+
if (item) return { item, path: wiPath };
|
|
564
|
+
} catch { /* best-effort lineage lookup */ }
|
|
565
|
+
}
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function resolveWorkItemScheduleContext(item, config) {
|
|
570
|
+
if (!item || typeof item !== 'object') return {};
|
|
571
|
+
const seen = new Set();
|
|
572
|
+
let current = item;
|
|
573
|
+
let scheduleId = current._scheduleId || current._sourceScheduleId || '';
|
|
574
|
+
let scheduleWorkItemId = current._scheduleId ? current.id : (current._scheduleWorkItemId || '');
|
|
575
|
+
let projectName = current.project || '';
|
|
576
|
+
let parentItemId = current.parent_id || current.parentId || '';
|
|
577
|
+
|
|
578
|
+
while ((!scheduleId || !projectName) && parentItemId && !seen.has(parentItemId)) {
|
|
579
|
+
seen.add(parentItemId);
|
|
580
|
+
const found = findWorkItemRecord(parentItemId, config);
|
|
581
|
+
if (!found?.item) break;
|
|
582
|
+
current = found.item;
|
|
583
|
+
if (!scheduleId && (current._scheduleId || current._sourceScheduleId)) {
|
|
584
|
+
scheduleId = current._scheduleId || current._sourceScheduleId;
|
|
585
|
+
scheduleWorkItemId = current._scheduleId ? current.id : (current._scheduleWorkItemId || current.id);
|
|
586
|
+
}
|
|
587
|
+
if (!projectName && current.project) projectName = current.project;
|
|
588
|
+
parentItemId = current.parent_id || current.parentId || '';
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return {
|
|
592
|
+
scheduleId: scheduleId || '',
|
|
593
|
+
scheduleWorkItemId: scheduleWorkItemId || '',
|
|
594
|
+
projectName: projectName || '',
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function applyScheduleContextToPrEntry(entry, item, config) {
|
|
599
|
+
const ctx = resolveWorkItemScheduleContext(item, config);
|
|
600
|
+
if (ctx.scheduleId) {
|
|
601
|
+
entry._sourceScheduleId = ctx.scheduleId;
|
|
602
|
+
if (ctx.scheduleWorkItemId) entry._scheduleWorkItemId = ctx.scheduleWorkItemId;
|
|
603
|
+
}
|
|
604
|
+
return entry;
|
|
605
|
+
}
|
|
606
|
+
|
|
549
607
|
/** Check if a work item is in a terminal completed state. */
|
|
550
608
|
function isItemCompleted(item) {
|
|
551
609
|
if (!item || typeof item !== 'object') return false;
|
|
@@ -920,6 +978,7 @@ function syncPrsFromOutput(output, agentId, meta, config, opts = {}) {
|
|
|
920
978
|
sourcePlan: meta?.item?.sourcePlan || '',
|
|
921
979
|
itemType: meta?.item?.itemType || ''
|
|
922
980
|
};
|
|
981
|
+
applyScheduleContextToPrEntry(entry, meta?.item, config);
|
|
923
982
|
// Issue #1772: one-off dispatches (e.g. human-initiated "review this PR" via CC)
|
|
924
983
|
// must not enroll the discovered PR into the auto eval loop. Tag _contextOnly so
|
|
925
984
|
// discoverFromPrs skips it for review/fix dispatch (still polled for status/comments).
|
|
@@ -1318,6 +1377,11 @@ function resolvePrFallbackProject(meta, config) {
|
|
|
1318
1377
|
const match = shared.resolveProjectSource(meta.item.project, projects, { allowCentral: false }).project;
|
|
1319
1378
|
if (match) return match;
|
|
1320
1379
|
}
|
|
1380
|
+
const scheduleContext = resolveWorkItemScheduleContext(meta?.item, config);
|
|
1381
|
+
if (scheduleContext.projectName) {
|
|
1382
|
+
const match = shared.resolveProjectSource(scheduleContext.projectName, projects, { allowCentral: false }).project;
|
|
1383
|
+
if (match) return match;
|
|
1384
|
+
}
|
|
1321
1385
|
return projects.length === 1 ? projects[0] : null;
|
|
1322
1386
|
}
|
|
1323
1387
|
|
|
@@ -1488,6 +1552,7 @@ function _attachFoundPrToWi(found, meta, agentId, resultSummary, config) {
|
|
|
1488
1552
|
sourcePlan: meta.item?.sourcePlan || '',
|
|
1489
1553
|
itemType: meta.item?.itemType || '',
|
|
1490
1554
|
};
|
|
1555
|
+
applyScheduleContextToPrEntry(entry, meta?.item, config);
|
|
1491
1556
|
shared.upsertPullRequestRecord(shared.projectPrPath(found.project), entry, {
|
|
1492
1557
|
project: found.project,
|
|
1493
1558
|
itemId: meta.item.id,
|
|
@@ -4184,6 +4249,9 @@ function processCompletionFollowups(completion, agentId, dispatchItem, config) {
|
|
|
4184
4249
|
}
|
|
4185
4250
|
if (raw.length === 0) return [];
|
|
4186
4251
|
const wiId = dispatchItem?.meta?.item?.id || 'N/A';
|
|
4252
|
+
const parentItem = dispatchItem?.meta?.item || null;
|
|
4253
|
+
const parentProject = dispatchItem?.meta?.project?.name || parentItem?.project || '';
|
|
4254
|
+
const parentSchedule = resolveWorkItemScheduleContext(parentItem, config);
|
|
4187
4255
|
let existingIds = null;
|
|
4188
4256
|
function loadExistingIds() {
|
|
4189
4257
|
if (existingIds !== null) return existingIds;
|
|
@@ -4217,6 +4285,27 @@ function processCompletionFollowups(completion, agentId, dispatchItem, config) {
|
|
|
4217
4285
|
if (allIds.size > 0 && !allIds.has(followupWiId)) {
|
|
4218
4286
|
log('warn', `Followup audit (${agentId || 'unknown'} wi=${wiId}): claimed followup ${followupWiId} not found in any work-items.json — dispatch may have been deduped, reverted, or never landed`);
|
|
4219
4287
|
}
|
|
4288
|
+
if (parentItem && (parentSchedule.scheduleId || parentProject)) {
|
|
4289
|
+
let stamped = false;
|
|
4290
|
+
for (const wiPath of getWorkItemPaths(config)) {
|
|
4291
|
+
mutateWorkItems(wiPath, items => {
|
|
4292
|
+
const target = Array.isArray(items) ? items.find(i => i && i.id === followupWiId) : null;
|
|
4293
|
+
if (!target) return items;
|
|
4294
|
+
if (!target.parent_id && target.id !== wiId) target.parent_id = wiId;
|
|
4295
|
+
if (!target.project && parentProject) target.project = parentProject;
|
|
4296
|
+
if (parentSchedule.scheduleId) {
|
|
4297
|
+
if (!target._sourceScheduleId) target._sourceScheduleId = parentSchedule.scheduleId;
|
|
4298
|
+
if (!target._scheduleWorkItemId) target._scheduleWorkItemId = parentSchedule.scheduleWorkItemId || wiId;
|
|
4299
|
+
}
|
|
4300
|
+
stamped = true;
|
|
4301
|
+
return items;
|
|
4302
|
+
});
|
|
4303
|
+
if (stamped) break;
|
|
4304
|
+
}
|
|
4305
|
+
if (stamped && parentSchedule.scheduleId) {
|
|
4306
|
+
log('info', `Followup audit (${agentId || 'unknown'} wi=${wiId}): linked ${followupWiId} to schedule ${parentSchedule.scheduleId}`);
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4220
4309
|
accepted.push({
|
|
4221
4310
|
wi_id: followupWiId,
|
|
4222
4311
|
title,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2152",
|
|
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"
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
"package:check": "node tools/check-package-boundary.js public",
|
|
10
10
|
"package:prepare:public": "node tools/prepare-package.js public",
|
|
11
11
|
"package:prepare:internal": "node tools/prepare-package.js internal",
|
|
12
|
+
"dev": "node bin/minions.js --dev restart",
|
|
13
|
+
"dev:start": "node bin/minions.js --dev start",
|
|
14
|
+
"dev:stop": "node bin/minions.js --dev stop",
|
|
15
|
+
"dev:dash": "node bin/minions.js --dev dash",
|
|
16
|
+
"dev:status": "node bin/minions.js --dev status",
|
|
12
17
|
"test": "node test/run-parallel.js",
|
|
13
18
|
"test:sequential": "node test/unit.test.js",
|
|
14
19
|
"test:unit": "node test/run-parallel.js",
|