@yemi33/minions 0.1.81 → 0.1.83
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/CHANGELOG.md +17 -0
- package/dashboard/js/command-center.js +39 -0
- package/dashboard.js +18 -1
- package/engine/lifecycle.js +37 -15
- package/package.json +1 -1
- package/playbooks/implement.md +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.83 (2026-03-31)
|
|
4
|
+
|
|
5
|
+
### Engine
|
|
6
|
+
- engine/lifecycle.js
|
|
7
|
+
|
|
8
|
+
## 0.1.82 (2026-03-31)
|
|
9
|
+
|
|
10
|
+
### Engine
|
|
11
|
+
- engine/lifecycle.js
|
|
12
|
+
|
|
13
|
+
### Dashboard
|
|
14
|
+
- dashboard.js
|
|
15
|
+
- dashboard/js/command-center.js
|
|
16
|
+
|
|
17
|
+
### Playbooks
|
|
18
|
+
- implement.md
|
|
19
|
+
|
|
3
20
|
## 0.1.81 (2026-03-31)
|
|
4
21
|
|
|
5
22
|
### Dashboard
|
|
@@ -384,6 +384,45 @@ async function ccExecuteAction(action) {
|
|
|
384
384
|
}
|
|
385
385
|
break;
|
|
386
386
|
}
|
|
387
|
+
case 'schedule': {
|
|
388
|
+
const url = action._update ? '/api/schedules/update' : '/api/schedules';
|
|
389
|
+
const res = await fetch(url, {
|
|
390
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
391
|
+
body: JSON.stringify({
|
|
392
|
+
id: action.id, title: action.title, cron: action.cron,
|
|
393
|
+
type: action.workType || 'implement',
|
|
394
|
+
project: action.project, agent: action.agent,
|
|
395
|
+
description: action.description, priority: action.priority,
|
|
396
|
+
enabled: action.enabled !== false,
|
|
397
|
+
})
|
|
398
|
+
});
|
|
399
|
+
if (!res.ok) { const d = await res.json().catch(() => ({})); throw new Error(d.error || 'Schedule create failed'); }
|
|
400
|
+
status.innerHTML = '✓ Schedule ' + (action._update ? 'updated' : 'created') + ': <strong>' + escHtml(action.id) + '</strong>';
|
|
401
|
+
status.style.color = 'var(--green)';
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
case 'delete-schedule': {
|
|
405
|
+
const res = await fetch('/api/schedules/delete', {
|
|
406
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
407
|
+
body: JSON.stringify({ id: action.id })
|
|
408
|
+
});
|
|
409
|
+
if (!res.ok) { const d = await res.json().catch(() => ({})); throw new Error(d.error || 'Schedule delete failed'); }
|
|
410
|
+
status.innerHTML = '✓ Deleted schedule: <strong>' + escHtml(action.id) + '</strong>';
|
|
411
|
+
status.style.color = 'var(--orange)';
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
case 'create-meeting': {
|
|
415
|
+
const res = await fetch('/api/meetings', {
|
|
416
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
417
|
+
body: JSON.stringify({ topic: action.topic, agents: action.agents, rounds: action.rounds, project: action.project })
|
|
418
|
+
});
|
|
419
|
+
if (!res.ok) { const d = await res.json().catch(() => ({})); throw new Error(d.error || 'Meeting create failed'); }
|
|
420
|
+
const d = await res.json();
|
|
421
|
+
status.innerHTML = '✓ Meeting started: <strong>' + escHtml(action.topic) + '</strong>' + (d.id ? ' (' + escHtml(d.id) + ')' : '');
|
|
422
|
+
status.style.color = 'var(--green)';
|
|
423
|
+
wakeEngine();
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
387
426
|
default:
|
|
388
427
|
status.innerHTML = '? Unknown action: ' + escHtml(action.type);
|
|
389
428
|
status.style.color = 'var(--muted)';
|
package/dashboard.js
CHANGED
|
@@ -371,6 +371,9 @@ Available action types:
|
|
|
371
371
|
- **plan-edit**: Revise/edit a plan .md file. Fields: file (plan .md filename from plans/), instruction (what to change).
|
|
372
372
|
- **execute-plan**: Execute an existing plan .md file. Fields: file (plan .md filename), project (optional)
|
|
373
373
|
- **file-edit**: Edit any minions file via LLM. Fields: file (path relative to minions dir), instruction (what to change).
|
|
374
|
+
- **schedule**: Create or update a scheduled task. Fields: id (unique slug), title, cron (3-field: minute hour dayOfWeek), workType (implement/test/explore/ask/review/fix), project (optional), agent (optional), description (optional), priority (optional), enabled (default true). Example cron: "0 9 2" = every Tuesday at 9am.
|
|
375
|
+
- **delete-schedule**: Delete a scheduled task. Fields: id.
|
|
376
|
+
- **create-meeting**: Start a team meeting. Fields: topic, agents (array of agent IDs), rounds (optional, default 3), project (optional).
|
|
374
377
|
|
|
375
378
|
## Rules
|
|
376
379
|
|
|
@@ -419,7 +422,8 @@ ${projects}
|
|
|
419
422
|
### Scheduled Tasks
|
|
420
423
|
${schedSummary}
|
|
421
424
|
|
|
422
|
-
|
|
425
|
+
### Dashboard API (all endpoints)
|
|
426
|
+
${_getApiRoutesSummary()}
|
|
423
427
|
|
|
424
428
|
For details on any of the above, use your tools to read files under \`${MINIONS_DIR}\`.`;
|
|
425
429
|
}
|
|
@@ -446,6 +450,16 @@ function parseCCActions(text) {
|
|
|
446
450
|
return { text: displayText, actions };
|
|
447
451
|
}
|
|
448
452
|
|
|
453
|
+
// ── API routes reference for CC — populated by server setup ──────────────────
|
|
454
|
+
let _apiRoutesRef = null; // set to ROUTES array once server initializes
|
|
455
|
+
function _getApiRoutesSummary() {
|
|
456
|
+
if (!_apiRoutesRef) return '(API routes not yet loaded — fetch GET /api/routes to discover endpoints)';
|
|
457
|
+
return _apiRoutesRef
|
|
458
|
+
.filter(r => r.path !== '/api/routes' && typeof r.path === 'string')
|
|
459
|
+
.map(r => `- \`${r.method} ${r.path}\` — ${r.desc}${r.params ? ' | Params: ' + r.params : ''}`)
|
|
460
|
+
.join('\n');
|
|
461
|
+
}
|
|
462
|
+
|
|
449
463
|
// ── Shared LLM call core — used by CC panel and doc modals ──────────────────
|
|
450
464
|
|
|
451
465
|
// Session store for doc modals — keyed by filePath or title, persisted to disk
|
|
@@ -3310,6 +3324,9 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3310
3324
|
{ method: 'POST', path: '/api/settings/routing', desc: 'Update routing.md', params: 'content', handler: handleSettingsRouting },
|
|
3311
3325
|
];
|
|
3312
3326
|
|
|
3327
|
+
// Expose routes to CC preamble builder (once, on first request)
|
|
3328
|
+
if (!_apiRoutesRef) _apiRoutesRef = ROUTES;
|
|
3329
|
+
|
|
3313
3330
|
// ── Route Dispatcher ────────────────────────────────────────────────────────
|
|
3314
3331
|
|
|
3315
3332
|
const pathname = req.url.split('?')[0];
|
package/engine/lifecycle.js
CHANGED
|
@@ -545,11 +545,10 @@ function syncPrsFromOutput(output, agentId, meta, config) {
|
|
|
545
545
|
|
|
546
546
|
const projects = shared.getProjects(config);
|
|
547
547
|
const defaultProject = (meta?.project?.name && projects.find(p => p.name === meta.project.name)) || projects[0];
|
|
548
|
-
|
|
548
|
+
const useCentral = !defaultProject;
|
|
549
549
|
|
|
550
550
|
// Match each PR to its correct project by finding which repo URL appears near the PR number in output
|
|
551
551
|
function resolveProjectForPr(prId) {
|
|
552
|
-
// Look for the PR URL in output to determine which ADO project it belongs to
|
|
553
552
|
for (const p of projects) {
|
|
554
553
|
if (!p.prUrlBase) continue;
|
|
555
554
|
const urlFragment = p.prUrlBase.replace(/pullrequest\/$/, '');
|
|
@@ -561,21 +560,32 @@ function syncPrsFromOutput(output, agentId, meta, config) {
|
|
|
561
560
|
return defaultProject;
|
|
562
561
|
}
|
|
563
562
|
|
|
563
|
+
// Extract PR URL directly from agent output — no manual construction
|
|
564
|
+
function extractPrUrl(prId) {
|
|
565
|
+
const ghMatch = output.match(new RegExp(`https?://github\\.com/[^\\s"'\\)\\]]*?/pull/${prId}(?:[^\\s"'\\)\\]]*)`, 'i'));
|
|
566
|
+
if (ghMatch) return ghMatch[0].replace(/[.,;:]+$/, '');
|
|
567
|
+
const adoMatch = output.match(new RegExp(`https?://(?:dev\\.azure\\.com|[^/]+\\.visualstudio\\.com)[^\\s"'\\)\\]]*?pullrequest/${prId}(?:[^\\s"'\\)\\]]*)`, 'i'));
|
|
568
|
+
if (adoMatch) return adoMatch[0].replace(/[.,;:]+$/, '');
|
|
569
|
+
return '';
|
|
570
|
+
}
|
|
571
|
+
|
|
564
572
|
const agentName = config.agents?.[agentId]?.name || agentId;
|
|
565
573
|
let added = 0;
|
|
566
|
-
|
|
567
|
-
|
|
574
|
+
const centralPrPath = path.join(MINIONS_DIR, 'pull-requests.json');
|
|
575
|
+
// Track which PR files need writing — keyed by target name
|
|
576
|
+
const dirtyTargets = new Map(); // name -> { prs, prPath }
|
|
568
577
|
|
|
569
578
|
for (const prId of prMatches) {
|
|
570
579
|
const fullId = `PR-${prId}`;
|
|
571
|
-
const targetProject = resolveProjectForPr(prId);
|
|
572
|
-
const
|
|
580
|
+
const targetProject = useCentral ? null : resolveProjectForPr(prId);
|
|
581
|
+
const targetName = targetProject ? targetProject.name : '_central';
|
|
582
|
+
const prPath = targetProject ? shared.projectPrPath(targetProject) : centralPrPath;
|
|
573
583
|
|
|
574
|
-
// Load PRs for this
|
|
575
|
-
if (!
|
|
576
|
-
|
|
584
|
+
// Load PRs for this target (cache per target)
|
|
585
|
+
if (!dirtyTargets.has(targetName)) {
|
|
586
|
+
dirtyTargets.set(targetName, { prs: safeJson(prPath) || [], prPath });
|
|
577
587
|
}
|
|
578
|
-
const entry =
|
|
588
|
+
const entry = dirtyTargets.get(targetName);
|
|
579
589
|
if (entry.prs.some(p => p.id === fullId || String(p.id).includes(prId))) continue;
|
|
580
590
|
|
|
581
591
|
let title = meta?.item?.title || '';
|
|
@@ -592,7 +602,7 @@ function syncPrsFromOutput(output, agentId, meta, config) {
|
|
|
592
602
|
reviewStatus: 'pending',
|
|
593
603
|
status: 'active',
|
|
594
604
|
created: e.dateStamp(),
|
|
595
|
-
url:
|
|
605
|
+
url: extractPrUrl(prId),
|
|
596
606
|
prdItems: meta?.item?.id ? [meta.item.id] : [],
|
|
597
607
|
sourcePlan: meta?.item?.sourcePlan || '',
|
|
598
608
|
itemType: meta?.item?.itemType || ''
|
|
@@ -601,9 +611,9 @@ function syncPrsFromOutput(output, agentId, meta, config) {
|
|
|
601
611
|
added++;
|
|
602
612
|
}
|
|
603
613
|
|
|
604
|
-
for (const [name, entry] of
|
|
614
|
+
for (const [name, entry] of dirtyTargets) {
|
|
605
615
|
shared.safeWrite(entry.prPath, entry.prs);
|
|
606
|
-
e.log('info', `Synced PR(s) from ${agentName}'s output to ${name}/pull-requests.json`);
|
|
616
|
+
e.log('info', `Synced PR(s) from ${agentName}'s output to ${name === '_central' ? 'central' : name}/pull-requests.json`);
|
|
607
617
|
}
|
|
608
618
|
return added;
|
|
609
619
|
}
|
|
@@ -1136,8 +1146,8 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
|
|
|
1136
1146
|
const projects = shared.getProjects(config);
|
|
1137
1147
|
const existingPrFound = Object.values(getPrLinks()).includes(meta.item.id);
|
|
1138
1148
|
if (!existingPrFound) {
|
|
1139
|
-
e.log('warn', `Agent completed implement task ${meta.item.id} but no PR was created`);
|
|
1140
|
-
//
|
|
1149
|
+
e.log('warn', `Agent completed implement task ${meta.item.id} but no PR was created — reverting to failed for retry`);
|
|
1150
|
+
// Revert to failed so auto-retry can re-attempt with PR creation
|
|
1141
1151
|
let wiPath;
|
|
1142
1152
|
if (meta.source === 'central-work-item' || meta.source === 'central-work-item-fanout') {
|
|
1143
1153
|
wiPath = path.join(MINIONS_DIR, 'work-items.json');
|
|
@@ -1149,6 +1159,18 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
|
|
|
1149
1159
|
const wi = items.find(i => i.id === meta.item.id);
|
|
1150
1160
|
if (wi) {
|
|
1151
1161
|
wi.noPr = true;
|
|
1162
|
+
wi.failReason = 'Completed without creating a pull request';
|
|
1163
|
+
const retries = wi._retryCount || 0;
|
|
1164
|
+
if (retries < 3) {
|
|
1165
|
+
wi.status = 'pending';
|
|
1166
|
+
wi._retryCount = retries + 1;
|
|
1167
|
+
delete wi.dispatched_at;
|
|
1168
|
+
delete wi.dispatched_to;
|
|
1169
|
+
e.log('info', `Auto-retry ${retries + 1}/3 for ${meta.item.id} (no PR created)`);
|
|
1170
|
+
} else {
|
|
1171
|
+
wi.status = 'failed';
|
|
1172
|
+
e.log('warn', `${meta.item.id} failed after 3 retries — no PR created`);
|
|
1173
|
+
}
|
|
1152
1174
|
shared.safeWrite(wiPath, items);
|
|
1153
1175
|
}
|
|
1154
1176
|
}
|
package/package.json
CHANGED
package/playbooks/implement.md
CHANGED
|
@@ -56,7 +56,9 @@ cd {{team_root}}
|
|
|
56
56
|
git worktree remove ../worktrees/{{branch_name}} --force
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
## Create PR
|
|
59
|
+
## Create PR (MANDATORY)
|
|
60
|
+
|
|
61
|
+
**Your task is NOT complete until a pull request exists.** If PR creation fails, retry up to 3 times before reporting the error.
|
|
60
62
|
|
|
61
63
|
{{pr_create_instructions}}
|
|
62
64
|
- sourceRefName: `refs/heads/{{branch_name}}`
|