@yemi33/minions 0.1.1678 → 0.1.1679
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 +5 -0
- package/dashboard.js +46 -3
- package/engine/copilot-models.json +1 -1
- package/engine/playbook.js +5 -1
- package/engine.js +52 -3
- package/package.json +1 -1
- package/prompts/cc-system.md +2 -2
package/CHANGELOG.md
CHANGED
package/dashboard.js
CHANGED
|
@@ -110,6 +110,22 @@ function normalizePrMetadata(metadata) {
|
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
function getWorkItemPrRef(input) {
|
|
114
|
+
if (!input || typeof input !== 'object') return null;
|
|
115
|
+
return input.targetPr || input.pr || input.prId || input.prNumber || input.pullRequest || input.sourcePr || input.prUrl || null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function copyWorkItemPrFields(item, input, pr = null) {
|
|
119
|
+
const prRef = getWorkItemPrRef(input);
|
|
120
|
+
if (!prRef && !pr) return;
|
|
121
|
+
const prNumber = pr ? shared.getPrNumber(pr) : shared.getPrNumber(prRef);
|
|
122
|
+
item.targetPr = pr?.id || prRef;
|
|
123
|
+
item.pr_id = pr?.id || (typeof prRef === 'string' ? prRef : '');
|
|
124
|
+
if (prNumber != null) item.prNumber = prNumber;
|
|
125
|
+
if (pr?.branch || input.prBranch) item.prBranch = pr?.branch || input.prBranch;
|
|
126
|
+
if (pr?.title || input.prTitle) item.prTitle = pr?.title || input.prTitle;
|
|
127
|
+
if (pr?.url || input.prUrl) item.prUrl = pr?.url || input.prUrl;
|
|
128
|
+
}
|
|
113
129
|
function linkPullRequestForTracking({ url, title, project: projectName, autoObserve, context, workItemId }, config = CONFIG, options = {}) {
|
|
114
130
|
if (!url) {
|
|
115
131
|
const err = new Error('url required');
|
|
@@ -1395,6 +1411,8 @@ async function executeCCActions(actions) {
|
|
|
1395
1411
|
const workType = routing.normalizeWorkType(action.workType || (action.type !== 'dispatch' ? action.type : WORK_TYPE.IMPLEMENT), WORK_TYPE.IMPLEMENT);
|
|
1396
1412
|
const id = 'W-' + shared.uid();
|
|
1397
1413
|
const project = action.project || '';
|
|
1414
|
+
const prRef = getWorkItemPrRef(action);
|
|
1415
|
+
let linkedPr = null;
|
|
1398
1416
|
|
|
1399
1417
|
// Strict project resolution. Silent fallback to PROJECTS[0] when the model named an unknown
|
|
1400
1418
|
// project caused work items to land in the wrong repo. Now: unknown name → error; ambiguous
|
|
@@ -1408,6 +1426,18 @@ async function executeCCActions(actions) {
|
|
|
1408
1426
|
results.push({ type: action.type, error: `Project "${project}" not found. Known projects: ${known}` });
|
|
1409
1427
|
break;
|
|
1410
1428
|
}
|
|
1429
|
+
} else if (prRef) {
|
|
1430
|
+
const allPrs = getPullRequests().filter(p => !p._ghost);
|
|
1431
|
+
linkedPr = shared.findPrRecord(allPrs, prRef) || null;
|
|
1432
|
+
if (linkedPr?._project && linkedPr._project !== 'central') {
|
|
1433
|
+
targetProject = PROJECTS.find(p => p.name?.toLowerCase() === String(linkedPr._project).toLowerCase()) || null;
|
|
1434
|
+
}
|
|
1435
|
+
if (!targetProject && PROJECTS.length > 1) {
|
|
1436
|
+
results.push({ type: action.type, error: `project field is required when ${PROJECTS.length} projects are configured: ${PROJECTS.map(p => p.name).join(', ')}` });
|
|
1437
|
+
break;
|
|
1438
|
+
} else if (!targetProject && PROJECTS.length === 1) {
|
|
1439
|
+
targetProject = PROJECTS[0];
|
|
1440
|
+
}
|
|
1411
1441
|
} else if (PROJECTS.length > 1) {
|
|
1412
1442
|
results.push({ type: action.type, error: `project field is required when ${PROJECTS.length} projects are configured: ${PROJECTS.map(p => p.name).join(', ')}` });
|
|
1413
1443
|
break;
|
|
@@ -1416,6 +1446,16 @@ async function executeCCActions(actions) {
|
|
|
1416
1446
|
}
|
|
1417
1447
|
// PROJECTS.length === 0 → targetProject stays null, falls back to root work-items.json (existing behavior).
|
|
1418
1448
|
|
|
1449
|
+
if (prRef && !linkedPr && targetProject) {
|
|
1450
|
+
const projectPrs = shared.safeJson(shared.projectPrPath(targetProject)) || [];
|
|
1451
|
+
shared.normalizePrRecords(projectPrs, targetProject);
|
|
1452
|
+
linkedPr = shared.findPrRecord(projectPrs, prRef, targetProject) || null;
|
|
1453
|
+
}
|
|
1454
|
+
if (prRef && (workType === WORK_TYPE.FIX || workType === WORK_TYPE.REVIEW || workType === WORK_TYPE.TEST) && !linkedPr) {
|
|
1455
|
+
results.push({ type: action.type, error: `PR not found: ${prRef}` });
|
|
1456
|
+
break;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1419
1459
|
const wiPath = targetProject ? shared.projectWorkItemsPath(targetProject) : path.join(MINIONS_DIR, 'work-items.json');
|
|
1420
1460
|
|
|
1421
1461
|
// Promote `agent` (singular) → `agents` (array). Models emit either shape and the prior code
|
|
@@ -1438,14 +1478,16 @@ async function executeCCActions(actions) {
|
|
|
1438
1478
|
const isOneShot = action.oneShot === true || (action.oneShot !== false && ccOneShotTypes.has(workType));
|
|
1439
1479
|
shared.mutateJsonFileLocked(wiPath, items => {
|
|
1440
1480
|
if (!Array.isArray(items)) items = [];
|
|
1441
|
-
|
|
1481
|
+
const item = {
|
|
1442
1482
|
id, title: action.title, type: workType,
|
|
1443
1483
|
priority: action.priority || 'medium', description: action.description || '',
|
|
1444
1484
|
status: WI_STATUS.PENDING, created: new Date().toISOString(),
|
|
1445
|
-
createdBy: 'command-center', project,
|
|
1485
|
+
createdBy: 'command-center', project: targetProject?.name || project,
|
|
1446
1486
|
...(agentHints.length ? { preferred_agent: agentHints[0], agents: agentHints } : {}),
|
|
1447
1487
|
...(isOneShot ? { oneShot: true } : {}),
|
|
1448
|
-
}
|
|
1488
|
+
};
|
|
1489
|
+
copyWorkItemPrFields(item, action, linkedPr);
|
|
1490
|
+
items.push(item);
|
|
1449
1491
|
return items;
|
|
1450
1492
|
}, { defaultValue: [] });
|
|
1451
1493
|
results.push({ type: action.type, id, ok: true });
|
|
@@ -2740,6 +2782,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
2740
2782
|
if (body.acceptanceCriteria) item.acceptanceCriteria = body.acceptanceCriteria;
|
|
2741
2783
|
if (body.skipPr) item.skipPr = true;
|
|
2742
2784
|
if (body.oneShot) item.oneShot = true;
|
|
2785
|
+
copyWorkItemPrFields(item, body);
|
|
2743
2786
|
let dupId = null;
|
|
2744
2787
|
mutateJsonFileLocked(wiPath, (items) => {
|
|
2745
2788
|
if (!Array.isArray(items)) items = [];
|
package/engine/playbook.js
CHANGED
|
@@ -628,9 +628,13 @@ function selectPlaybook(workType, item) {
|
|
|
628
628
|
if (workType === WORK_TYPE.IMPLEMENT || workType === WORK_TYPE.IMPLEMENT_LARGE) {
|
|
629
629
|
return 'implement';
|
|
630
630
|
}
|
|
631
|
-
|
|
631
|
+
const hasPrContext = !!(item?._pr || item?.pr_id || item?.targetPr || item?.sourcePr || item?.pr);
|
|
632
|
+
if (workType === WORK_TYPE.REVIEW && !hasPrContext) {
|
|
632
633
|
return 'work-item';
|
|
633
634
|
}
|
|
635
|
+
if (workType === WORK_TYPE.FIX && hasPrContext) {
|
|
636
|
+
return 'fix';
|
|
637
|
+
}
|
|
634
638
|
const typeSpecificPlaybooks = ['explore', 'review', 'test', 'plan-to-prd', 'plan', 'ask', 'verify', 'decompose', 'docs', 'meeting-investigate', 'meeting-debate', 'meeting-conclude'];
|
|
635
639
|
return typeSpecificPlaybooks.includes(workType) ? workType : 'work-item';
|
|
636
640
|
}
|
package/engine.js
CHANGED
|
@@ -2592,6 +2592,14 @@ function renderProjectWorkItemPromptForAgent(item, workType, agentId, config, pr
|
|
|
2592
2592
|
worktree_path: path.resolve(root, config.engine?.worktreeRoot || '../worktrees', `${branchName}`),
|
|
2593
2593
|
commit_message: item.commitMessage || `feat: ${item.title || item.id}`,
|
|
2594
2594
|
notes_content: '',
|
|
2595
|
+
pr_id: item.pr_id || item._pr || item.targetPr || item.sourcePr || item.pr || '',
|
|
2596
|
+
pr_number: item.prNumber || item.pr_number || '',
|
|
2597
|
+
pr_title: item.pr_title || item.prTitle || '',
|
|
2598
|
+
pr_branch: item.pr_branch || item.prBranch || '',
|
|
2599
|
+
pr_author: item.pr_author || item.prAuthor || '',
|
|
2600
|
+
pr_url: item.pr_url || item.prUrl || '',
|
|
2601
|
+
reviewer: item.reviewer || 'Reviewer',
|
|
2602
|
+
review_note: item.review_note || item.reviewNote || item.description || item.title || 'See PR thread comments',
|
|
2595
2603
|
};
|
|
2596
2604
|
const cpResult = buildWorkItemDispatchVars(item, vars, config, {
|
|
2597
2605
|
worktreePath: vars.worktree_path || root,
|
|
@@ -2612,6 +2620,38 @@ function renderProjectWorkItemPromptForAgent(item, workType, agentId, config, pr
|
|
|
2612
2620
|
};
|
|
2613
2621
|
}
|
|
2614
2622
|
|
|
2623
|
+
function getWorkItemPrRef(item) {
|
|
2624
|
+
if (!item || typeof item !== 'object') return null;
|
|
2625
|
+
return item.targetPr || item.pr || item.pr_id || item.sourcePr || item.pullRequest || item.prUrl || item.prNumber || null;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
function resolveWorkItemPrRecord(item, project) {
|
|
2629
|
+
if (!project) return null;
|
|
2630
|
+
const prRef = getWorkItemPrRef(item);
|
|
2631
|
+
if (!prRef) return null;
|
|
2632
|
+
const prs = safeJson(projectPrPath(project)) || [];
|
|
2633
|
+
shared.normalizePrRecords(prs, project);
|
|
2634
|
+
return shared.findPrRecord(prs, prRef, project);
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
function withWorkItemPrContext(item, pr) {
|
|
2638
|
+
if (!pr) return item;
|
|
2639
|
+
const prNumber = shared.getPrNumber(pr);
|
|
2640
|
+
return {
|
|
2641
|
+
...item,
|
|
2642
|
+
_pr: pr.id,
|
|
2643
|
+
pr_id: pr.id,
|
|
2644
|
+
targetPr: item.targetPr || pr.id,
|
|
2645
|
+
prNumber: prNumber ?? item.prNumber,
|
|
2646
|
+
pr_number: prNumber ?? item.pr_number,
|
|
2647
|
+
pr_title: pr.title || item.pr_title || item.prTitle || '',
|
|
2648
|
+
pr_branch: pr.branch || item.pr_branch || item.prBranch || '',
|
|
2649
|
+
pr_author: pr.agent || item.pr_author || item.prAuthor || '',
|
|
2650
|
+
pr_url: pr.url || item.pr_url || item.prUrl || '',
|
|
2651
|
+
reviewer: item.reviewer || 'Reviewer',
|
|
2652
|
+
review_note: item.review_note || item.reviewNote || item.description || item.title || 'See PR thread comments',
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2615
2655
|
function projectFromDispatchMeta(metaProject, config) {
|
|
2616
2656
|
if (!metaProject) return null;
|
|
2617
2657
|
const projects = getProjects(config);
|
|
@@ -2775,8 +2815,17 @@ function discoverFromWorkItems(config, project) {
|
|
|
2775
2815
|
skipped.noAgent++; continue;
|
|
2776
2816
|
}
|
|
2777
2817
|
|
|
2818
|
+
const linkedPr = resolveWorkItemPrRecord(item, project);
|
|
2819
|
+
const promptItem = linkedPr ? withWorkItemPrContext(item, linkedPr) : item;
|
|
2820
|
+
const prBranch = linkedPr?.branch || '';
|
|
2821
|
+
const isPrTargeted = !!(linkedPr && (workType === WORK_TYPE.FIX || workType === WORK_TYPE.REVIEW || workType === WORK_TYPE.TEST));
|
|
2822
|
+
if (!linkedPr && getWorkItemPrRef(item) && (workType === WORK_TYPE.FIX || workType === WORK_TYPE.REVIEW || workType === WORK_TYPE.TEST)) {
|
|
2823
|
+
if (item._pendingReason !== 'pr_not_found') { item._pendingReason = 'pr_not_found'; needsWrite = true; }
|
|
2824
|
+
log('warn', `Work item ${item.id} references PR ${getWorkItemPrRef(item)} but no tracked PR record was found`);
|
|
2825
|
+
continue;
|
|
2826
|
+
}
|
|
2778
2827
|
const isShared = item.branchStrategy === 'shared-branch' && item.featureBranch;
|
|
2779
|
-
const branchName = isShared ? item.featureBranch : (item.branch || `work/${item.id}`);
|
|
2828
|
+
const branchName = isPrTargeted && prBranch ? prBranch : (isShared ? item.featureBranch : (item.branch || `work/${item.id}`));
|
|
2780
2829
|
const deferredAgentResolution = agentId === routing.ANY_AGENT;
|
|
2781
2830
|
|
|
2782
2831
|
// Branch mutex: skip if target branch is locked by an active dispatch
|
|
@@ -2789,7 +2838,7 @@ function discoverFromWorkItems(config, project) {
|
|
|
2789
2838
|
}
|
|
2790
2839
|
|
|
2791
2840
|
const promptAgentId = deferredAgentResolution ? reservedAgentId : agentId;
|
|
2792
|
-
const promptResult = renderProjectWorkItemPromptForAgent(
|
|
2841
|
+
const promptResult = renderProjectWorkItemPromptForAgent(promptItem, workType, promptAgentId, config, project, root, branchName);
|
|
2793
2842
|
if (promptResult.needsReview) {
|
|
2794
2843
|
log('warn', `Work item ${item.id} exceeded 3 checkpoint-resumes — marking as failed for manual intervention`);
|
|
2795
2844
|
item.status = WI_STATUS.FAILED;
|
|
@@ -2827,7 +2876,7 @@ function discoverFromWorkItems(config, project) {
|
|
|
2827
2876
|
agentRole: config.agents[agentId]?.role || tempAgents.get(agentId)?.role || 'Agent',
|
|
2828
2877
|
task: `[${project?.name || 'project'}] ${item.title || item.description?.slice(0, 80) || item.id}`,
|
|
2829
2878
|
prompt,
|
|
2830
|
-
meta: { dispatchKey: key, source: 'work-item', branch: branchName, branchStrategy: item.branchStrategy || 'parallel', useExistingBranch: !!(item.branchStrategy === 'shared-branch' && item.featureBranch), item, project: { name: project?.name, localPath: project?.localPath }, deferAgentResolution: deferredAgentResolution }
|
|
2879
|
+
meta: { dispatchKey: key, source: 'work-item', branch: branchName, branchStrategy: item.branchStrategy || 'parallel', useExistingBranch: !!(isPrTargeted || (item.branchStrategy === 'shared-branch' && item.featureBranch)), item: promptItem, project: { name: project?.name, localPath: project?.localPath }, deferAgentResolution: deferredAgentResolution, ...(linkedPr ? { pr: linkedPr } : {}) }
|
|
2831
2880
|
});
|
|
2832
2881
|
|
|
2833
2882
|
setCooldown(key);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1679",
|
|
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
|
@@ -80,8 +80,8 @@ I'll dispatch dallas to fix that bug.
|
|
|
80
80
|
- `knowledge`: `title`, `content`, and `category` REQUIRED. Valid categories: architecture, conventions, project-notes, build-reports, reviews.
|
|
81
81
|
|
|
82
82
|
Core action types:
|
|
83
|
-
- **dispatch**: title (REQUIRED), workType, priority (low/medium/high), agents[] or agent (optional — both shapes accepted), project (REQUIRED when multi-project), description
|
|
84
|
-
workTypes: `explore` (research/report only, NO PR), `ask` (answer/report, NO PR), `implement` (new code, PR REQUIRED), `fix` (bug fix
|
|
83
|
+
- **dispatch**: title (REQUIRED), workType, priority (low/medium/high), agents[] or agent (optional — both shapes accepted), project (REQUIRED when multi-project unless `pr` resolves to a tracked PR), description, pr (optional PR number/id/url for work that targets an existing PR)
|
|
84
|
+
workTypes: `explore` (research/report only, NO PR), `ask` (answer/report, NO PR), `implement` (new code, PR REQUIRED), `fix` (standalone bug fix creates a PR; include `pr` when fixing review comments/build failures on an existing PR), `review` (code review, NO PR), `test` (tests, PR if new), `verify` (merge/build/maintenance, NO PR)
|
|
85
85
|
If the user wants a design/architecture artifact committed through a PR, dispatch `implement` or `docs` rather than `explore`.
|
|
86
86
|
When the user names a specific agent ("assign this to lambert"), put exactly that one name in `agents` (e.g. `"agents": ["lambert"]`). A single-agent assignment is hard-pinned by the server — it will queue for that agent only and skip the routing table. Use multi-agent arrays only when the user names multiple agents or asks for fan-out.
|
|
87
87
|
- **build-and-test**: pr, project (optional), agent (optional) — Run the build-and-test playbook against a PR. The agent will checkout the PR branch, run the project's build/test commands, and report results. Use when the user asks to "run tests on PR X" or "build PR X" or after a fix to verify nothing regressed.
|