@yemi33/minions 0.1.2018 → 0.1.2020
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-meetings.js +46 -2
- package/dashboard/js/render-plans.js +11 -0
- package/engine/shared.js +12 -10
- package/engine.js +27 -2
- package/package.json +1 -1
|
@@ -115,6 +115,9 @@ let _meetingPollId = null;
|
|
|
115
115
|
function _stopMeetingPoll() {
|
|
116
116
|
if (_meetingPollInterval) { clearInterval(_meetingPollInterval); _meetingPollInterval = null; }
|
|
117
117
|
_meetingPollId = null;
|
|
118
|
+
// F2 (W-mpgb0xe6000gb66d): drop linked-plan recheck cache when the modal closes.
|
|
119
|
+
_modalMeetingForLinkedPlanRecheck = null;
|
|
120
|
+
_modalLastLinkedPlanFile = null;
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
function _renderMeetingDetail(m) {
|
|
@@ -227,6 +230,10 @@ function _renderMeetingDetail(m) {
|
|
|
227
230
|
try { showModalQa(); } catch { /* expected if QA not loaded */ }
|
|
228
231
|
|
|
229
232
|
document.getElementById('modal').classList.add('open');
|
|
233
|
+
|
|
234
|
+
// F2 (W-mpgb0xe6000gb66d): refresh the linked-plan cache after each
|
|
235
|
+
// render so refreshPlans() can detect a flip and re-render the modal.
|
|
236
|
+
_setModalMeetingForLinkedPlanRecheck(m);
|
|
230
237
|
}
|
|
231
238
|
|
|
232
239
|
function openMeetingDetail(id) {
|
|
@@ -401,7 +408,14 @@ function _viewPlanWithBack(file, meetingId) {
|
|
|
401
408
|
}
|
|
402
409
|
|
|
403
410
|
function _findLinkedPlan(meeting) {
|
|
404
|
-
|
|
411
|
+
// F2 (W-mpgb0xe6000gb66d): prefer window._lastPlans (refreshed by refreshPlans
|
|
412
|
+
// every ~12s) over window._lastStatus.plans, which is gated on the next
|
|
413
|
+
// /api/status fetch and can lag a newly-created plan by an entire refresh
|
|
414
|
+
// cycle. Falling back to _lastStatus.plans preserves first-paint behavior
|
|
415
|
+
// before refreshPlans has run.
|
|
416
|
+
var plans = (Array.isArray(window._lastPlans) ? window._lastPlans : null)
|
|
417
|
+
|| window._lastStatus?.plans
|
|
418
|
+
|| [];
|
|
405
419
|
// Check 1: regex in conclusion text (agent may reference plan path)
|
|
406
420
|
if (meeting?.conclusion?.content) {
|
|
407
421
|
var match = meeting.conclusion.content.match(/plans\/([\w-]+\.md)/);
|
|
@@ -419,6 +433,36 @@ function _findLinkedPlan(meeting) {
|
|
|
419
433
|
return null;
|
|
420
434
|
}
|
|
421
435
|
|
|
436
|
+
// F2 (W-mpgb0xe6000gb66d): post-refreshPlans hook. Cache the meeting
|
|
437
|
+
// currently displayed in the modal so refreshPlans() can re-evaluate
|
|
438
|
+
// _findLinkedPlan and re-render the action row when a freshly-created plan
|
|
439
|
+
// now matches. Without this, the modal poll loop (3s) only re-renders on
|
|
440
|
+
// meeting hash changes, so a plan created in the background never flips
|
|
441
|
+
// "Create Plan from Meeting" → "View Plan".
|
|
442
|
+
var _modalMeetingForLinkedPlanRecheck = null;
|
|
443
|
+
var _modalLastLinkedPlanFile = null;
|
|
444
|
+
|
|
445
|
+
function _setModalMeetingForLinkedPlanRecheck(meeting) {
|
|
446
|
+
if (meeting && meeting.status === 'completed') {
|
|
447
|
+
_modalMeetingForLinkedPlanRecheck = meeting;
|
|
448
|
+
_modalLastLinkedPlanFile = _findLinkedPlan(meeting)?.file || null;
|
|
449
|
+
} else {
|
|
450
|
+
_modalMeetingForLinkedPlanRecheck = null;
|
|
451
|
+
_modalLastLinkedPlanFile = null;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function _rerenderMeetingLinkedPlanIfChanged() {
|
|
456
|
+
if (!_modalMeetingForLinkedPlanRecheck) return;
|
|
457
|
+
var modalOpen = document.getElementById('modal')?.classList?.contains('open');
|
|
458
|
+
if (!modalOpen || !_meetingPollId) return;
|
|
459
|
+
var nextFile = _findLinkedPlan(_modalMeetingForLinkedPlanRecheck)?.file || null;
|
|
460
|
+
if (nextFile === _modalLastLinkedPlanFile) return;
|
|
461
|
+
// Linked-plan resolution changed — re-render the modal (preserves scroll;
|
|
462
|
+
// _renderMeetingDetail will refresh _modalLastLinkedPlanFile via the cache hook).
|
|
463
|
+
_renderMeetingDetail(_modalMeetingForLinkedPlanRecheck);
|
|
464
|
+
}
|
|
465
|
+
|
|
422
466
|
async function _createPlanFromMeeting(id, btn) {
|
|
423
467
|
// Optimistic: instant feedback before fetching the meeting + posting the WI.
|
|
424
468
|
showToast('meeting-plan-toast', 'Plan task queued — agent will draft the plan from this meeting', true);
|
|
@@ -483,4 +527,4 @@ async function _deleteMeeting(id) {
|
|
|
483
527
|
} catch (e) { clearDeleted('mtg:' + id); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
484
528
|
}
|
|
485
529
|
|
|
486
|
-
window.MinionsMeetings = { renderMeetings, openMeetingDetail, openCreateMeetingModal };
|
|
530
|
+
window.MinionsMeetings = { renderMeetings, openMeetingDetail, openCreateMeetingModal, _rerenderMeetingLinkedPlanIfChanged };
|
|
@@ -65,6 +65,17 @@ async function refreshPlans() {
|
|
|
65
65
|
// colliding with the 'plans' slice tracked by _processStatusUpdate (F6).
|
|
66
66
|
if (typeof _changed !== 'function' || _changed('plansPayload', plans)) {
|
|
67
67
|
renderPlans(plans);
|
|
68
|
+
// F2 (W-mpgb0xe6000gb66d): if a meeting modal is open, ask it to re-check
|
|
69
|
+
// its linked-plan resolution against the freshly fetched plan list. Without
|
|
70
|
+
// this hook, a meeting modal's "Create Plan from Meeting" → "View Plan"
|
|
71
|
+
// flip waits for the next meeting-object change (refresh.js:135 gates
|
|
72
|
+
// renderMeetings on _changed(meetings, ...)). Gated on _changed so we only
|
|
73
|
+
// re-render meeting modal when plan data actually moved (F6 alignment).
|
|
74
|
+
try {
|
|
75
|
+
if (typeof _rerenderMeetingLinkedPlanIfChanged === 'function') {
|
|
76
|
+
_rerenderMeetingLinkedPlanIfChanged();
|
|
77
|
+
}
|
|
78
|
+
} catch { /* hook is best-effort — never block plan refresh on it */ }
|
|
68
79
|
}
|
|
69
80
|
} catch (e) { console.error('plans refresh:', e.message); }
|
|
70
81
|
}
|
package/engine/shared.js
CHANGED
|
@@ -3255,16 +3255,18 @@ function getOperatorLogin(config) {
|
|
|
3255
3255
|
}
|
|
3256
3256
|
|
|
3257
3257
|
function deriveWorkItemBranchName(item, config) {
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
//
|
|
3264
|
-
//
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3258
|
+
// Reverted to the pre-#2698 short form `work/<wi-id>` after the
|
|
3259
|
+
// descriptive-slug convention `user/<loginname>/<wi-id>-<slug>` produced
|
|
3260
|
+
// 80–120-char branch names that polluted derived identifiers (e.g. the
|
|
3261
|
+
// conflict-fix WI id in engine.js#buildDepConflictFixItem) and made the
|
|
3262
|
+
// dashboard unreadable. The function signature is preserved so call sites
|
|
3263
|
+
// and the dashboard pre-compute path stay wired. `getOperatorLogin` /
|
|
3264
|
+
// `config` are intentionally unused here but retained for callers that
|
|
3265
|
+
// may still inspect them.
|
|
3266
|
+
void config;
|
|
3267
|
+
const wid = String(item?.id || '').trim();
|
|
3268
|
+
if (!wid) return null;
|
|
3269
|
+
return sanitizeBranch(`work/${wid}`);
|
|
3268
3270
|
}
|
|
3269
3271
|
|
|
3270
3272
|
function _worktreeNameSuffix(dispatchId, projectName, branchName) {
|
package/engine.js
CHANGED
|
@@ -214,6 +214,31 @@ function parseConflictFiles(mergeOutput) {
|
|
|
214
214
|
// be spawned in the blocked item's worktree root. That is a PRE-EXISTING
|
|
215
215
|
// limitation of the dep-conflict-fix path — single-project plans (the common
|
|
216
216
|
// case, and the trigger scenario P-wi1-bridge-readonly-{a,b,c}) are unaffected.
|
|
217
|
+
// Derive a SHORT, stable conflict-fix WI id from a branch name. The legacy
|
|
218
|
+
// shape `conflict-fix-${branch.replace(/[^a-zA-Z0-9-]/g, '-')}` was fine when
|
|
219
|
+
// branches were `work/<wi-id>` (max ~25 chars), but PR #2698 codified the
|
|
220
|
+
// `user/<loginname>/<wi-id>-<slug>` convention which makes branch names
|
|
221
|
+
// 80-120+ chars and the resulting WI ids unreadable in the dashboard.
|
|
222
|
+
//
|
|
223
|
+
// Strategy:
|
|
224
|
+
// - strip `user/<login>/` prefix (only the WI portion matters)
|
|
225
|
+
// - strip `work/` prefix → keep `work-` for legacy-branch shape parity
|
|
226
|
+
// - sanitize remaining `/` → `-`
|
|
227
|
+
// - cap at 40 chars total; if truncated, suffix with 8-char sha1 of the
|
|
228
|
+
// full branch so different long branches that share a 40-char prefix
|
|
229
|
+
// still get distinct (deterministic) ids
|
|
230
|
+
function deriveConflictFixKey(branch) {
|
|
231
|
+
let s = String(branch || '');
|
|
232
|
+
const userMatch = s.match(/^user\/[^/]+\/(.+)$/);
|
|
233
|
+
if (userMatch) s = userMatch[1];
|
|
234
|
+
const workMatch = s.match(/^work\/(.+)$/);
|
|
235
|
+
if (workMatch) s = `work-${workMatch[1]}`;
|
|
236
|
+
s = s.replace(/[^a-zA-Z0-9-]/g, '-');
|
|
237
|
+
if (s.length <= 40) return s;
|
|
238
|
+
const hash = require('crypto').createHash('sha1').update(String(branch || '')).digest('hex').slice(0, 8);
|
|
239
|
+
return `${s.slice(0, 31)}-${hash}`;
|
|
240
|
+
}
|
|
241
|
+
|
|
217
242
|
function buildDepConflictFixItem({
|
|
218
243
|
depConflictBranch,
|
|
219
244
|
depConflictFiles = [],
|
|
@@ -225,7 +250,7 @@ function buildDepConflictFixItem({
|
|
|
225
250
|
}) {
|
|
226
251
|
if (!depConflictBranch) throw new Error('buildDepConflictFixItem: depConflictBranch is required');
|
|
227
252
|
if (!mainBranch) throw new Error('buildDepConflictFixItem: mainBranch is required');
|
|
228
|
-
const conflictFixId = `conflict-fix-${depConflictBranch
|
|
253
|
+
const conflictFixId = `conflict-fix-${deriveConflictFixKey(depConflictBranch)}`;
|
|
229
254
|
const filesDesc = depConflictFiles.length > 0
|
|
230
255
|
? `\n\nConflicting files:\n${depConflictFiles.map(f => '- ' + f).join('\n')}`
|
|
231
256
|
: '';
|
|
@@ -6758,7 +6783,7 @@ module.exports = {
|
|
|
6758
6783
|
reconcileItemsWithPrs, detectDependencyCycles,
|
|
6759
6784
|
areDependenciesMet, // exported for testing (P-bf04-decompose-zero-children)
|
|
6760
6785
|
parseConflictFiles, pruneAncestorDeps, preflightMergeSimulation, // exported for testing
|
|
6761
|
-
buildDepConflictFixItem, // exported for testing (W-mpcwojgr000a0244)
|
|
6786
|
+
buildDepConflictFixItem, deriveConflictFixKey, // exported for testing (W-mpcwojgr000a0244)
|
|
6762
6787
|
isWorktreeRetryableError, removeStaleIndexLock, syncReusedWorktree, assertCleanSharedWorktree, // exported for testing
|
|
6763
6788
|
pruneStaleWorktreeForBranch, // exported for testing
|
|
6764
6789
|
findExistingWorktree, // exported for testing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2020",
|
|
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"
|