@yemi33/minions 0.1.777 → 0.1.779

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.779 (2026-04-10)
4
+
5
+ ### Features
6
+ - auto-complete PRs when builds green + review approved
7
+
8
+ ## 0.1.778 (2026-04-10)
9
+
10
+ ### Fixes
11
+ - agent comments included in context but don't trigger fix dispatch
12
+
3
13
  ## 0.1.777 (2026-04-10)
4
14
 
5
15
  ### Fixes
@@ -32,11 +32,10 @@
32
32
  <span id="cc-session-info" style="font-size:10px;color:var(--muted)">New session</span>
33
33
  </div>
34
34
  <div style="display:flex;align-items:center;gap:8px">
35
- <button onclick="ccNewTab()" style="background:none;border:1px solid var(--border);color:var(--muted);font-size:10px;padding:2px 8px;border-radius:3px;cursor:pointer">New Chat</button>
35
+ <button onclick="ccNewSession()" style="background:none;border:1px solid var(--border);color:var(--muted);font-size:10px;padding:2px 8px;border-radius:3px;cursor:pointer">Clear Chat</button>
36
36
  <button onclick="toggleCommandCenter()" style="background:none;border:none;color:var(--muted);font-size:16px;cursor:pointer;padding:2px 6px">&#10005;</button>
37
37
  </div>
38
38
  </div>
39
- <div id="cc-tab-bar" style="display:flex;gap:4px;padding:4px 12px;border-bottom:1px solid var(--border);overflow-x:auto;align-items:center;flex-shrink:0"></div>
40
39
  <div id="cc-messages" style="flex:1;overflow-y:auto;padding:12px 16px;display:flex;flex-direction:column;gap:10px"></div>
41
40
  <div style="padding:12px 16px;border-top:1px solid var(--border)">
42
41
  <div style="display:flex;gap:8px">
@@ -99,7 +98,6 @@
99
98
  <div class="modal-header">
100
99
  <h3 id="modal-title">—</h3>
101
100
  <div class="modal-header-actions">
102
- <button class="modal-copy" id="modal-back-btn" onclick="modalGoBack()" title="Back" style="display:none">&larr; Back</button>
103
101
  <button class="modal-copy" id="modal-edit-btn" onclick="modalToggleEdit()" title="Edit" style="display:none"><svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M11.013 1.427a1.75 1.75 0 012.474 0l1.086 1.086a1.75 1.75 0 010 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 01-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.61zM12.9 2.97L4.288 11.58l-.537 1.878 1.878-.537L14.242 4.31 12.9 2.97z"/></svg> Edit</button>
104
102
  <button class="modal-copy" id="modal-save-btn" onclick="modalSaveEdit()" title="Save" style="display:none;color:var(--green);border-color:var(--green)"><svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"/></svg> Save</button>
105
103
  <button class="modal-copy" id="modal-cancel-edit-btn" onclick="modalCancelEdit()" title="Cancel edit" style="display:none">Cancel</button>
@@ -113,14 +111,10 @@
113
111
  <div id="modal-qa-thread-wrap" style="display:none">
114
112
  <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
115
113
  <button id="qa-clear-btn" style="background:none;border:none;color:var(--muted);font-size:10px;cursor:pointer;padding:2px 6px" onclick="clearQaConversation()" title="Clear conversation">Clear</button>
116
- <button id="qa-toggle-btn" style="background:none;border:none;color:var(--muted);font-size:10px;cursor:pointer;padding:2px 6px" onclick="toggleDocChat()" title="Collapse thread">&#x25BC; Collapse</button>
114
+ <button style="background:none;border:none;color:var(--muted);font-size:10px;cursor:pointer;padding:2px 6px" onclick="toggleDocChat()" id="qa-toggle-btn" title="Collapse thread">&#x25BC; Collapse</button>
117
115
  </div>
118
116
  <div class="modal-qa-thread" id="modal-qa-thread"></div>
119
117
  </div>
120
- <div id="qa-expand-bar" style="display:none;margin-bottom:4px">
121
- <button style="background:none;border:none;color:var(--muted);font-size:10px;cursor:pointer;padding:2px 6px" onclick="toggleDocChat()">&#x25B2; Show thread</button>
122
- </div>
123
- <div id="qa-action-slot"></div>
124
118
  <div class="modal-qa-selection-pill" id="modal-qa-pill" style="display:none">
125
119
  <span class="pill-label">Selection:</span>
126
120
  <span class="pill-text" id="modal-qa-pill-text"></span>
package/engine/ado.js CHANGED
@@ -418,6 +418,31 @@ async function pollPrStatus(config) {
418
418
  }
419
419
  }
420
420
 
421
+ // Auto-complete: set auto-complete on PR when builds green + review approved
422
+ if (pr.status === PR_STATUS.ACTIVE && pr.reviewStatus === 'approved' && pr.buildStatus === 'passing' && !pr._autoCompleted) {
423
+ const autoComplete = config.engine?.autoCompletePrs !== false;
424
+ if (autoComplete) {
425
+ try {
426
+ const mergeStrategy = config.engine?.prMergeMethod === 'merge' ? 1 : config.engine?.prMergeMethod === 'rebase' ? 2 : 3; // 3 = squash
427
+ const identityUrl = `${orgBase}/_apis/connectionData?api-version=7.1`;
428
+ const identity = await adoFetch(identityUrl, token).catch(() => null);
429
+ const autoCompleteUrl = `${orgBase}/${project.adoProject}/_apis/git/repositories/${project.repositoryId}/pullrequests/${prNum}?api-version=7.1`;
430
+ await adoFetch(autoCompleteUrl, token, {
431
+ method: 'PATCH',
432
+ body: JSON.stringify({
433
+ autoCompleteSetBy: { id: identity?.authenticatedUser?.id },
434
+ completionOptions: { mergeStrategy, deleteSourceBranch: true, transitionWorkItems: true }
435
+ })
436
+ });
437
+ pr._autoCompleted = true;
438
+ log('info', `Auto-complete set on PR ${pr.id}: builds green + review approved`);
439
+ updated = true;
440
+ } catch (e) {
441
+ log('warn', `Auto-complete failed for PR ${pr.id}: ${e.message}`);
442
+ }
443
+ }
444
+ }
445
+
421
446
  return updated;
422
447
  } catch (err) {
423
448
  // Auth errors → mark build status stale so dashboard shows uncertainty
@@ -459,35 +484,37 @@ async function pollPrHumanComments(config) {
459
484
  for (const comment of (thread.comments || [])) {
460
485
  if (!comment.content || comment.commentType === 'system') continue;
461
486
  const content = comment.content;
462
- // Minions agent signatures
463
- if (/\bMinions\s*\(/i.test(content)) continue;
464
- if (/\b(Review|Fixed|Explored|Verified|Tested)\s+by\s+Minions\b/i.test(content)) continue;
465
- if (/\[minions\]/i.test(content)) continue;
466
- // Skip bot/service account comments
487
+ // Skip bots and CI noise
467
488
  const authorName = (comment.author?.displayName || '').toLowerCase();
468
489
  if (/\b(bot|service|build|pipeline|codecov|sonar)\b/i.test(authorName)) continue;
469
- // Short comments from same author that look agent-generated
470
- if (content.length < 500 && /\b(fixed|addressed|resolved|pushed|updated|rebased)\b/i.test(content) && /\b(review|feedback|comment|finding)\b/i.test(content)) continue;
471
- // Automated status comments (coverage, build reports)
472
490
  if (/^#{1,3}\s*(Coverage|Build|Test|Deploy|Pipeline)\s*(Report|Status|Result|Summary)/i.test(content)) continue;
491
+ // Detect agent comments (included in context, but don't trigger fix)
492
+ const isAgent = /\bMinions\s*\(/i.test(content) || /\bby\s+Minions\b/i.test(content) || /\[minions\]/i.test(content);
473
493
 
474
494
  const entry = {
475
495
  threadId: thread.id,
476
496
  commentId: comment.id,
477
497
  author: comment.author?.displayName || 'Human',
478
498
  content: comment.content,
479
- date: comment.publishedDate
499
+ date: comment.publishedDate,
500
+ _isAgent: isAgent
480
501
  };
481
502
  allHumanComments.push(entry);
482
503
 
483
- // Track which comments are new (for triggering any new comment triggers a fix)
504
+ // Only non-agent new comments trigger a fix (agent reviews trigger via vote)
484
505
  const commentMs = comment.publishedDate ? new Date(comment.publishedDate).getTime() : 0;
485
- if (commentMs && commentMs > cutoffMs) {
506
+ if (commentMs && commentMs > cutoffMs && !isAgent) {
486
507
  newHumanComments.push(entry);
487
508
  }
488
509
  }
489
510
  }
490
511
 
512
+ // Update cutoff even if only agent comments are new
513
+ const allNewDates = allHumanComments.filter(c => (new Date(c.date).getTime() || 0) > cutoffMs).map(c => c.date);
514
+ if (allNewDates.length > 0 && newHumanComments.length === 0) {
515
+ pr.humanFeedback = { ...(pr.humanFeedback || {}), lastProcessedCommentDate: allNewDates.sort().pop() };
516
+ return false;
517
+ }
491
518
  if (newHumanComments.length === 0) return false;
492
519
 
493
520
  // Sort all comments chronologically and build full context for the fix agent
package/engine/github.js CHANGED
@@ -418,6 +418,22 @@ async function pollPrStatus(config) {
418
418
  }
419
419
  }
420
420
 
421
+ // Auto-complete: merge PR when builds green + review approved
422
+ if (pr.status === PR_STATUS.ACTIVE && pr.reviewStatus === 'approved' && pr.buildStatus === 'passing' && !pr._autoCompleted) {
423
+ const autoComplete = config.engine?.autoCompletePrs !== false; // default: on
424
+ if (autoComplete) {
425
+ try {
426
+ const mergeMethod = config.engine?.prMergeMethod || 'squash';
427
+ await execAsync(`gh pr merge ${prNum} --${mergeMethod} --repo ${slug} --delete-branch`, { timeout: 30000, encoding: 'utf-8' });
428
+ pr._autoCompleted = true;
429
+ log('info', `Auto-completed PR ${pr.id}: builds green + review approved → merged (${mergeMethod})`);
430
+ updated = true;
431
+ } catch (e) {
432
+ log('warn', `Auto-complete failed for PR ${pr.id}: ${e.message}`);
433
+ }
434
+ }
435
+ }
436
+
421
437
  return updated;
422
438
  });
423
439
 
@@ -441,27 +457,25 @@ async function pollPrHumanComments(config) {
441
457
  ...(Array.isArray(reviewComments) ? reviewComments : []).map(c => ({ ...c, _type: 'review' }))
442
458
  ];
443
459
 
444
- // Filter out bot comments, minions agent comments, and CI noise
445
- // Agents post as the PAT owner detect by signature patterns, not account type
446
- const prAuthorLogin = (prData?.user?.login || '').toLowerCase();
447
- const humanComments = allComments.filter(c => {
448
- if (c.user?.type === 'Bot') return false;
449
- const body = c.body || '';
450
- // Minions agent signatures (review, fix, explore, etc.)
451
- if (/\bMinions\s*\(/i.test(body)) return false;
452
- if (/\b(Review|Fixed|Explored|Verified|Tested)\s+by\s+Minions\b/i.test(body)) return false;
453
- if (/\[minions\]/i.test(body)) return false;
454
- // Comments from the PR author account are likely agent-generated (agents use same PAT)
455
- // Only skip if the comment looks automated (short, or has agent patterns)
460
+ // Separate: agent comments (included in context, don't trigger fix) vs human comments (trigger fix)
461
+ // All non-bot, non-CI comments go into context. Only non-agent comments trigger pendingFix.
462
+ function _isBot(c) {
463
+ if (c.user?.type === 'Bot') return true;
456
464
  const login = (c.user?.login || '').toLowerCase();
457
- if (login === prAuthorLogin && body.length < 500 && /\b(fixed|addressed|resolved|pushed|updated|rebased)\b/i.test(body)) return false;
458
- // Common CI bot usernames
459
- if (/\b(bot|codecov|sonar|dependabot|renovate|github-actions|azure-pipelines)\b/i.test(login)) return false;
460
- // Automated status comments (coverage reports, build badges, etc.)
461
- if (/^#{1,3}\s*(Coverage|Build|Test|Deploy|Pipeline)\s*(Report|Status|Result|Summary)/i.test(body)) return false;
462
- if (/!\[.*\]\(https?:\/\/.*badge/i.test(body)) return false;
463
- return true;
464
- });
465
+ if (/\b(bot|codecov|sonar|dependabot|renovate|github-actions|azure-pipelines)\b/i.test(login)) return true;
466
+ const body = c.body || '';
467
+ if (/^#{1,3}\s*(Coverage|Build|Test|Deploy|Pipeline)\s*(Report|Status|Result|Summary)/i.test(body)) return true;
468
+ if (/!\[.*\]\(https?:\/\/.*badge/i.test(body)) return true;
469
+ return false;
470
+ }
471
+ function _isAgentComment(c) {
472
+ const body = c.body || '';
473
+ if (/\bMinions\s*\(/i.test(body)) return true;
474
+ if (/\bby\s+Minions\b/i.test(body)) return true;
475
+ if (/\[minions\]/i.test(body)) return true;
476
+ return false;
477
+ }
478
+ const humanComments = allComments.filter(c => !_isBot(c));
465
479
 
466
480
  const cutoffStr = pr.humanFeedback?.lastProcessedCommentDate || pr.created || '1970-01-01';
467
481
  const cutoffMs = new Date(cutoffStr).getTime() || 0;
@@ -472,21 +486,29 @@ async function pollPrHumanComments(config) {
472
486
 
473
487
  for (const c of humanComments) {
474
488
  const date = c.created_at || c.updated_at || '';
489
+ const isAgent = _isAgentComment(c);
475
490
  const entry = {
476
491
  commentId: c.id,
477
492
  author: c.user?.login || 'Human',
478
493
  content: c.body || '',
479
- date
494
+ date,
495
+ _isAgent: isAgent
480
496
  };
481
497
  allCommentEntries.push(entry);
482
498
 
483
- // Any new comment triggers a fix no @minions filter needed
499
+ // Only non-agent new comments trigger a fix (agent reviews trigger via vote, not comment)
484
500
  const dateMs = date ? new Date(date).getTime() : 0;
485
- if (dateMs && dateMs > cutoffMs) {
501
+ if (dateMs && dateMs > cutoffMs && !isAgent) {
486
502
  newComments.push(entry);
487
503
  }
488
504
  }
489
505
 
506
+ // Update cutoff even if only agent comments are new (so we don't re-scan them)
507
+ const allNewDates = allCommentEntries.filter(c => (new Date(c.date).getTime() || 0) > cutoffMs).map(c => c.date);
508
+ if (allNewDates.length > 0 && newComments.length === 0) {
509
+ pr.humanFeedback = { ...(pr.humanFeedback || {}), lastProcessedCommentDate: allNewDates.sort().pop() };
510
+ return false; // agent comments only — don't trigger fix
511
+ }
490
512
  if (newComments.length === 0) return false;
491
513
 
492
514
  // Sort all comments chronologically and build full context for the fix agent
package/engine/shared.js CHANGED
@@ -547,6 +547,8 @@ const ENGINE_DEFAULTS = {
547
547
  lockRetryBackoffMs: 500, // base backoff between lock retries (doubles each attempt: 500ms, 1s, 2s, ...)
548
548
  maxBuildFixAttempts: 3, // max consecutive auto-fix dispatch cycles per PR before escalation to human
549
549
  buildFixGracePeriod: 600000, // 10min — wait for CI to run after build fix before re-dispatching
550
+ autoCompletePrs: true, // auto-merge PRs when builds green + review approved
551
+ prMergeMethod: 'squash', // merge method: squash, merge, rebase
550
552
  ccModel: 'sonnet', // model for Command Center and doc-chat (sonnet, haiku, opus)
551
553
  ccEffort: null, // effort level for CC/doc-chat (null, 'low', 'medium', 'high')
552
554
  ccMaxTurns: 50, // max tool-use turns for CC/doc-chat before CLI stops
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.777",
3
+ "version": "0.1.779",
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"