@yemi33/minions 0.1.1000 → 0.1.1002

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,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.1000 (2026-04-15)
3
+ ## 0.1.1002 (2026-04-15)
4
4
 
5
5
  ### Features
6
6
  - fix stopAfter=0 watch expiry — run forever for all conditions (#1117)
@@ -25,6 +25,8 @@
25
25
  - gate auto-fix conflict dispatch behind autoFixConflicts flag
26
26
 
27
27
  ### Fixes
28
+ - extend auto-link fallback to ADO projects
29
+ - auto-link existing GitHub PR when agent completes without creating one
28
30
  - preserve ordered lists across blank lines in markdown renderer
29
31
  - restore CC rendering and tab bar
30
32
  - restore WATCH_ABSOLUTE_CONDITIONS import in watches.js lost in #1117 squash merge
@@ -43,8 +45,6 @@
43
45
  - address review feedback — move writeToInbox outside lock, add absolute condition auto-expire
44
46
  - fix watches feature gaps — human notifications, branch stub, status-change init, unique keys
45
47
  - skip isAlreadyDispatched in needsReReview to allow re-review within 1hr
46
- - skip SessionStart hook settings test on CI
47
- - loop queue flush so messages queued during combined send aren't orphaned
48
48
 
49
49
  ### Other
50
50
  - docs: update CLAUDE.md with recent Minions architecture changes
package/engine/ado.js CHANGED
@@ -890,6 +890,29 @@ const isAdoThrottled = () => _adoThrottle.isThrottled();
890
890
  /** Returns a snapshot of the current throttle state. Calls isAdoThrottled() for a fresh value. */
891
891
  const getAdoThrottleState = () => _adoThrottle.getState();
892
892
 
893
+ /**
894
+ * Query ADO for an open PR on a specific branch.
895
+ * Used as a last-resort fallback when an agent completes without logging a PR URL
896
+ * but a PR may already exist from a prior orphaned dispatch.
897
+ * @param {object} project — project config with adoOrg, adoProject, repositoryId, prUrlBase
898
+ * @param {string} branch — source branch name (without refs/heads/ prefix)
899
+ * @returns {{ prNumber: number, url: string }|null}
900
+ */
901
+ async function findOpenPrOnBranch(project, branch) {
902
+ if (!project.adoOrg || !project.adoProject || !project.repositoryId || !branch) return null;
903
+ const token = await getAdoToken();
904
+ if (!token) return null;
905
+ const orgBase = shared.getAdoOrgBase(project);
906
+ const sourceRef = encodeURIComponent(`refs/heads/${branch}`);
907
+ const url = `${orgBase}/${project.adoProject}/_apis/git/repositories/${project.repositoryId}/pullrequests?searchCriteria.status=active&searchCriteria.sourceRefName=${sourceRef}&api-version=7.1`;
908
+ const data = await adoFetch(url, token);
909
+ const pr = (data.value || [])[0];
910
+ if (!pr) return null;
911
+ const prNumber = pr.pullRequestId;
912
+ const prUrl = project.prUrlBase ? `${project.prUrlBase}${prNumber}` : `https://dev.azure.com/${project.adoOrg}/${project.adoProject}/_git/${project.repositoryId}/pullrequest/${prNumber}`;
913
+ return { prNumber, url: prUrl };
914
+ }
915
+
893
916
  /** Reset throttle state — exported for testing only. */
894
917
  function _resetAdoThrottle() {
895
918
  _adoThrottle._reset();
@@ -913,6 +936,7 @@ module.exports = {
913
936
  getAdoThrottleState,
914
937
  fetchAdoPrMetadata,
915
938
  fetchSinglePrBuildStatus,
939
+ findOpenPrOnBranch,
916
940
  _resetAdoThrottle, // exported for testing
917
941
  _setAdoThrottleForTest, // exported for testing
918
942
  };
@@ -1792,6 +1792,46 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
1792
1792
  }
1793
1793
  }
1794
1794
  }
1795
+ // Last resort: query the platform directly for an open PR on this branch.
1796
+ // Handles the case where a prior orphaned dispatch created a PR but the engine
1797
+ // never processed its output — so the PR exists on the platform but not in pull-requests.json.
1798
+ if (!existingPrFound && meta?.branch) {
1799
+ const projectObj = shared.getProjects(config).find(p => p.name === meta?.project?.name);
1800
+ if (projectObj) {
1801
+ try {
1802
+ let found = null;
1803
+ if (projectObj.repoHost === 'github') {
1804
+ const ghSlug = projectObj.prUrlBase?.match(/github\.com\/([^/]+\/[^/]+)\/pull/)?.[1];
1805
+ if (ghSlug) {
1806
+ const raw = await execAsync(`gh pr list --head "${meta.branch}" --repo ${ghSlug} --json number,url,state --limit 1`, { timeout: 15000, windowsHide: true });
1807
+ const hits = JSON.parse(raw || '[]');
1808
+ if (hits.length > 0 && hits[0].state === 'OPEN') found = { prNumber: hits[0].number, url: hits[0].url };
1809
+ }
1810
+ } else {
1811
+ found = await require('./ado').findOpenPrOnBranch(projectObj, meta.branch);
1812
+ }
1813
+ if (found) {
1814
+ const fullId = `PR-${found.prNumber}`;
1815
+ const prPath = shared.projectPrPath(projectObj);
1816
+ mutateJsonFileLocked(prPath, prs => {
1817
+ if (!Array.isArray(prs)) prs = [];
1818
+ if (prs.some(p => p.id === fullId)) return prs;
1819
+ prs.push({
1820
+ id: fullId, prNumber: found.prNumber, title: meta.item?.title || '',
1821
+ agent: agentId, branch: meta.branch, reviewStatus: 'pending',
1822
+ status: PR_STATUS.ACTIVE, created: ts(), url: found.url,
1823
+ prdItems: meta.item?.id ? [meta.item.id] : [],
1824
+ sourcePlan: meta.item?.sourcePlan || '', itemType: meta.item?.itemType || '',
1825
+ });
1826
+ return prs;
1827
+ });
1828
+ if (meta.item?.id) addPrLink(fullId, meta.item.id);
1829
+ log('info', `Auto-linked existing PR ${fullId} on branch ${meta.branch} for ${meta.item?.id}`);
1830
+ existingPrFound = true;
1831
+ }
1832
+ } catch (e) { log('warn', `PR lookup for branch ${meta.branch}: ${e.message}`); }
1833
+ }
1834
+ }
1795
1835
  if (!existingPrFound) {
1796
1836
  const noPrWiPath = resolveWorkItemPath(meta);
1797
1837
  if (noPrWiPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1000",
3
+ "version": "0.1.1002",
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"