@yemi33/minions 0.1.1654 → 0.1.1656
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 +6 -0
- package/dashboard.js +53 -10
- package/engine/ado.js +45 -8
- package/engine/copilot-models.json +1 -1
- package/engine/lifecycle.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/dashboard.js
CHANGED
|
@@ -75,7 +75,42 @@ function getWorkItemIdFromPrLinkContext(context, workItemId) {
|
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
function
|
|
78
|
+
function decodeUrlSegment(segment) {
|
|
79
|
+
try { return decodeURIComponent(segment); } catch { return segment; }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function parseAdoPrMetadataTarget(url) {
|
|
83
|
+
const raw = String(url || '');
|
|
84
|
+
const devAzure = raw.match(/https?:\/\/dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\/([^/]+)\/pullrequest\/(\d+)/i);
|
|
85
|
+
if (devAzure) {
|
|
86
|
+
return {
|
|
87
|
+
adoOrg: decodeUrlSegment(devAzure[1]),
|
|
88
|
+
adoProj: decodeUrlSegment(devAzure[2]),
|
|
89
|
+
adoRepo: decodeUrlSegment(devAzure[3]),
|
|
90
|
+
prNum: devAzure[4],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const visualStudio = raw.match(/https?:\/\/([^/.]+)\.visualstudio\.com\/(?:DefaultCollection\/)?([^/]+)\/_git\/([^/]+)\/pullrequest\/(\d+)/i);
|
|
94
|
+
if (!visualStudio) return null;
|
|
95
|
+
return {
|
|
96
|
+
adoOrg: `${decodeUrlSegment(visualStudio[1])}.visualstudio.com`,
|
|
97
|
+
adoProj: decodeUrlSegment(visualStudio[2]),
|
|
98
|
+
adoRepo: decodeUrlSegment(visualStudio[3]),
|
|
99
|
+
prNum: visualStudio[4],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function normalizePrMetadata(metadata) {
|
|
104
|
+
if (!metadata || typeof metadata !== 'object') return null;
|
|
105
|
+
return {
|
|
106
|
+
title: typeof metadata.title === 'string' ? metadata.title.trim() : '',
|
|
107
|
+
description: typeof metadata.description === 'string' ? metadata.description : '',
|
|
108
|
+
branch: typeof metadata.branch === 'string' ? metadata.branch.trim().replace(/^refs\/heads\//i, '') : '',
|
|
109
|
+
author: typeof metadata.author === 'string' ? metadata.author.trim() : '',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function linkPullRequestForTracking({ url, title, project: projectName, autoObserve, context, workItemId }, config = CONFIG, options = {}) {
|
|
79
114
|
if (!url) {
|
|
80
115
|
const err = new Error('url required');
|
|
81
116
|
err.statusCode = 400;
|
|
@@ -90,13 +125,14 @@ function linkPullRequestForTracking({ url, title, project: projectName, autoObse
|
|
|
90
125
|
const prId = shared.getCanonicalPrId(targetProject, prNum, url);
|
|
91
126
|
const linkedWorkItemId = getWorkItemIdFromPrLinkContext(context, workItemId);
|
|
92
127
|
const contextText = typeof context === 'string' ? context : (context == null ? '' : JSON.stringify(context));
|
|
128
|
+
const metadata = normalizePrMetadata(options.metadata);
|
|
93
129
|
const result = shared.upsertPullRequestRecord(prPath, {
|
|
94
130
|
id: prId,
|
|
95
131
|
prNumber: parseInt(prNum, 10) || null,
|
|
96
|
-
title: (title || 'PR #' + prNum + ' (polling...)').slice(0, 120),
|
|
97
|
-
description: '',
|
|
98
|
-
agent: 'human',
|
|
99
|
-
branch: '',
|
|
132
|
+
title: (metadata?.title || title || 'PR #' + prNum + ' (polling...)').slice(0, 120),
|
|
133
|
+
description: metadata?.description ? metadata.description.slice(0, 500) : '',
|
|
134
|
+
agent: metadata?.author || 'human',
|
|
135
|
+
branch: metadata?.branch || '',
|
|
100
136
|
reviewStatus: 'pending',
|
|
101
137
|
status: 'active',
|
|
102
138
|
created: new Date().toISOString(),
|
|
@@ -5994,7 +6030,16 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5994
6030
|
if (!url) return jsonReply(res, 400, { error: 'url required' });
|
|
5995
6031
|
|
|
5996
6032
|
reloadConfig();
|
|
5997
|
-
const
|
|
6033
|
+
const adoTarget = parseAdoPrMetadataTarget(url);
|
|
6034
|
+
let initialPrData = null;
|
|
6035
|
+
if (adoTarget) {
|
|
6036
|
+
try {
|
|
6037
|
+
initialPrData = await ado.fetchAdoPrMetadata(adoTarget.prNum, adoTarget.adoOrg, adoTarget.adoProj, adoTarget.adoRepo);
|
|
6038
|
+
} catch (e) {
|
|
6039
|
+
shared.log('warn', `ADO PR link metadata fetch failed for ${url}: ${e.message}`);
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
const { id: prId, prPath, prNum, created, linked } = linkPullRequestForTracking(body, CONFIG, { metadata: initialPrData });
|
|
5998
6043
|
invalidateStatusCache();
|
|
5999
6044
|
jsonReply(res, 200, { ok: true, id: prId, created, linked });
|
|
6000
6045
|
|
|
@@ -6003,16 +6048,14 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
6003
6048
|
try {
|
|
6004
6049
|
let prData = null;
|
|
6005
6050
|
const ghMatch = url.match(/github\.com\/([^/]+\/[^/]+)\/pull\/(\d+)/);
|
|
6006
|
-
const adoMatch = url.match(/dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\/([^/]+)\/pullrequest\/(\d+)/);
|
|
6007
6051
|
if (ghMatch) {
|
|
6008
6052
|
const slug = ghMatch[1];
|
|
6009
6053
|
const result = await shared.execAsync(`gh api "repos/${slug}/pulls/${prNum}"`, { timeout: 15000, encoding: 'utf-8' });
|
|
6010
6054
|
const d = JSON.parse(result);
|
|
6011
6055
|
prData = { title: d.title, description: d.body, branch: d.head?.ref, author: d.user?.login };
|
|
6012
|
-
} else if (
|
|
6013
|
-
const [, adoOrg, adoProj, adoRepo] = adoMatch;
|
|
6056
|
+
} else if (adoTarget && !initialPrData) {
|
|
6014
6057
|
try {
|
|
6015
|
-
prData = await ado.fetchAdoPrMetadata(prNum, adoOrg, adoProj, adoRepo);
|
|
6058
|
+
prData = await ado.fetchAdoPrMetadata(adoTarget.prNum, adoTarget.adoOrg, adoTarget.adoProj, adoTarget.adoRepo);
|
|
6016
6059
|
} catch { /* ADO token may not be available */ }
|
|
6017
6060
|
}
|
|
6018
6061
|
if (!prData) return;
|
package/engine/ado.js
CHANGED
|
@@ -138,6 +138,48 @@ function clearBuildStatusStale(pr) {
|
|
|
138
138
|
if (pr._buildStatusDetail) delete pr._buildStatusDetail;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
function isPlaceholderPrTitle(title, prNum) {
|
|
142
|
+
const text = String(title || '').trim();
|
|
143
|
+
if (!text) return true;
|
|
144
|
+
if (/\bpolling\.\.\./i.test(text)) return true;
|
|
145
|
+
if (String(prNum || '') && (text === `PR #${prNum}` || text === `PR-${prNum}`)) return true;
|
|
146
|
+
if (/[{}"\[\]]/.test(text)) return true;
|
|
147
|
+
return /^[0-9a-f-]{8,}$/i.test(text);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function applyAdoPrMetadata(pr, prData, prNum) {
|
|
151
|
+
if (!pr || !prData) return false;
|
|
152
|
+
let updated = false;
|
|
153
|
+
|
|
154
|
+
const sourceBranch = stripRefsHeads(prData.sourceRefName);
|
|
155
|
+
if (sourceBranch && (pr.branch !== sourceBranch || pr._branchResolutionError || pr._pendingReason === shared.PR_PENDING_REASON.MISSING_BRANCH)) {
|
|
156
|
+
pr.branch = sourceBranch;
|
|
157
|
+
if (pr._branchResolutionError) delete pr._branchResolutionError;
|
|
158
|
+
if (pr._pendingReason === shared.PR_PENDING_REASON.MISSING_BRANCH) delete pr._pendingReason;
|
|
159
|
+
updated = true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const title = String(prData.title || '').trim();
|
|
163
|
+
if (title && isPlaceholderPrTitle(pr.title, prNum) && pr.title !== title.slice(0, 120)) {
|
|
164
|
+
pr.title = title.slice(0, 120);
|
|
165
|
+
updated = true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const description = typeof prData.description === 'string' ? prData.description : '';
|
|
169
|
+
if (description && (pr.description == null || pr.description === '')) {
|
|
170
|
+
pr.description = description.slice(0, 500);
|
|
171
|
+
updated = true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const author = String(prData.createdBy?.displayName || '').trim();
|
|
175
|
+
if (author && pr.agent === 'human') {
|
|
176
|
+
pr.agent = author;
|
|
177
|
+
updated = true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return updated;
|
|
181
|
+
}
|
|
182
|
+
|
|
141
183
|
// ── Build/Review Status Helpers ───────────────────────────────────────────────
|
|
142
184
|
|
|
143
185
|
/** Classify an array of ADO build records into a single status string. */
|
|
@@ -454,13 +496,7 @@ async function pollPrStatus(config) {
|
|
|
454
496
|
|
|
455
497
|
const prData = await adoFetch(`${repoBase}?api-version=7.1`, token);
|
|
456
498
|
|
|
457
|
-
|
|
458
|
-
if (sourceBranch && pr.branch !== sourceBranch) {
|
|
459
|
-
pr.branch = sourceBranch;
|
|
460
|
-
if (pr._branchResolutionError) delete pr._branchResolutionError;
|
|
461
|
-
if (pr._pendingReason === 'missing_pr_branch') delete pr._pendingReason;
|
|
462
|
-
updated = true;
|
|
463
|
-
}
|
|
499
|
+
if (applyAdoPrMetadata(pr, prData, prNum)) updated = true;
|
|
464
500
|
|
|
465
501
|
let newStatus = pr.status;
|
|
466
502
|
if (prData.status === 'completed') newStatus = PR_STATUS.MERGED;
|
|
@@ -1121,7 +1157,8 @@ async function checkLiveBuildAndConflict(pr, project) {
|
|
|
1121
1157
|
async function fetchAdoPrMetadata(prNum, adoOrg, adoProj, adoRepo) {
|
|
1122
1158
|
const token = await getAdoToken();
|
|
1123
1159
|
if (!token) return null;
|
|
1124
|
-
const
|
|
1160
|
+
const orgBase = getAdoOrgBase({ adoOrg });
|
|
1161
|
+
const url = `${orgBase}/${encodeURIComponent(adoProj)}/_apis/git/repositories/${encodeURIComponent(adoRepo)}/pullrequests/${encodeURIComponent(String(prNum))}?api-version=7.1`;
|
|
1125
1162
|
const pr = await adoFetch(url, token);
|
|
1126
1163
|
if (!pr) return null;
|
|
1127
1164
|
return {
|
package/engine/lifecycle.js
CHANGED
|
@@ -2260,7 +2260,7 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
|
|
|
2260
2260
|
if (nonTerminalCompletion) {
|
|
2261
2261
|
skipDoneStatus = true;
|
|
2262
2262
|
const reason = deferNonTerminalCompletion(meta, nonTerminalCompletion);
|
|
2263
|
-
completionContractFailure = { reason, itemId: meta.item.id, nonTerminal: true };
|
|
2263
|
+
completionContractFailure = { reason, itemId: meta.item.id, nonTerminal: true, processWorkItemFailure: false };
|
|
2264
2264
|
if (!nonCleanReportWritten) {
|
|
2265
2265
|
writeNonCleanAgentReport(dispatchItem, agentId, 'partial', structuredCompletion, completionGateSummary, code);
|
|
2266
2266
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1656",
|
|
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"
|