@yemi33/minions 0.1.397 → 0.1.399

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.399 (2026-04-06)
4
+
5
+ ### Fixes
6
+ - auto-detect main branch when configured mainBranch doesn't exist
7
+ - engine restart button shows toast + green checkmark, suppresses stale banner 30s
8
+
3
9
  ## 0.1.397 (2026-04-06)
4
10
 
5
11
  ### Fixes
@@ -37,7 +37,7 @@ function renderEngineStatus(engine) {
37
37
  function renderEngineAlert(state, staleMs) {
38
38
  const el = document.getElementById('engine-alert');
39
39
  if (!el) return;
40
- if (state !== 'stale') {
40
+ if (state !== 'stale' || (window._engineRestartedAt && Date.now() - window._engineRestartedAt < 30000)) {
41
41
  el.style.display = 'none';
42
42
  el.innerHTML = '';
43
43
  return;
@@ -53,7 +53,12 @@ function renderEngineAlert(state, staleMs) {
53
53
  const res = await fetch('/api/engine/restart', { method: 'POST' });
54
54
  const data = await res.json();
55
55
  if (data.ok) {
56
- this.textContent = 'Restarted (PID ' + data.pid + ')';
56
+ this.textContent = '\u2713 Restarted (PID ' + data.pid + ')';
57
+ this.style.color = 'var(--green)';
58
+ this.style.borderColor = 'var(--green)';
59
+ showToast('cmd-toast', 'Engine restarted — PID ' + data.pid, true);
60
+ // Suppress stale banner for 30s while new engine writes its first heartbeat
61
+ window._engineRestartedAt = Date.now();
57
62
  setTimeout(() => refresh(), 3000);
58
63
  } else {
59
64
  this.textContent = 'Failed: ' + (data.error || 'unknown');
package/engine/cli.js CHANGED
@@ -735,7 +735,7 @@ const commands = {
735
735
  agent_role: config.agents[agentId]?.role,
736
736
  project_name: targetProject.name || 'Unknown',
737
737
  project_path: targetProject.localPath || '',
738
- main_branch: targetProject.mainBranch || 'main',
738
+ main_branch: targetProject.localPath ? shared.resolveMainBranch(targetProject.localPath, targetProject.mainBranch) : (targetProject.mainBranch || 'main'),
739
739
  ado_org: targetProject.adoOrg || 'Unknown',
740
740
  ado_project: targetProject.adoProject || 'Unknown',
741
741
  repo_name: targetProject.repoName || 'Unknown',
@@ -166,7 +166,7 @@ function checkPlanCompletion(meta, config) {
166
166
  if (!existingPrItem) {
167
167
  const id = 'PL-' + shared.uid();
168
168
  const featureBranch = plan.feature_branch;
169
- const mainBranch = primaryProject.mainBranch || 'main';
169
+ const mainBranch = shared.resolveMainBranch(primaryProject.localPath, primaryProject.mainBranch);
170
170
  const itemSummary = doneItems.map(w => '- ' + w.id + ': ' + w.title.replace('Implement: ', '')).join('\n');
171
171
  workItems.push({
172
172
  id, title: `Create PR for plan: ${plan.plan_summary || planFile}`,
@@ -196,7 +196,7 @@ function checkPlanCompletion(meta, config) {
196
196
  return pr.status === PR_STATUS.ACTIVE && linkedId && doneItems.find(w => w.id === linkedId);
197
197
  });
198
198
  if (prs.length > 0) {
199
- projectPrs[p.name] = { project: p, prs, mainBranch: p.mainBranch || 'main' };
199
+ projectPrs[p.name] = { project: p, prs, mainBranch: shared.resolveMainBranch(p.localPath, p.mainBranch) };
200
200
  }
201
201
  }
202
202
 
@@ -30,7 +30,7 @@ function getPrCreateInstructions(project) {
30
30
  if (host === 'github') {
31
31
  const org = project?.adoOrg || '';
32
32
  const repo = project?.repoName || '';
33
- const mainBranch = project?.mainBranch || 'main';
33
+ const mainBranch = project?.localPath ? shared.resolveMainBranch(project.localPath, project.mainBranch) : (project?.mainBranch || 'main');
34
34
  return `Use \`gh pr create\` to create a pull request:\n` +
35
35
  `- \`gh pr create --base ${mainBranch} --head <your-branch> --title "PR title" --body "PR description" --repo ${org}/${repo}\`\n` +
36
36
  `- Always set --base to \`${mainBranch}\` (the main branch)\n` +
@@ -62,7 +62,7 @@ function getPrFetchInstructions(project) {
62
62
  if (host === 'github') {
63
63
  const org = project?.adoOrg || '';
64
64
  const repo = project?.repoName || '';
65
- const mainBranch = project?.mainBranch || 'main';
65
+ const mainBranch = project?.localPath ? shared.resolveMainBranch(project.localPath, project.mainBranch) : (project?.mainBranch || 'main');
66
66
  return `Use \`gh pr view\` to fetch PR status:\n` +
67
67
  `- \`gh pr view <number> --json number,title,state,mergeable,reviewDecision,headRefName,baseRefName,statusCheckRollup --repo ${org}/${repo}\`\n` +
68
68
  `- This returns JSON with PR state, mergeability, review decision, and check statuses\n` +
package/engine/shared.js CHANGED
@@ -267,6 +267,51 @@ function execSilent(cmd, opts = {}) {
267
267
  return _execSync(cmd, { stdio: 'pipe', windowsHide: true, ...opts });
268
268
  }
269
269
 
270
+ /**
271
+ * Detect the default branch for a git repo. Tries in order:
272
+ * 1. The configured mainBranch (if it exists as a local or remote ref)
273
+ * 2. git symbolic-ref refs/remotes/origin/HEAD (what the remote says)
274
+ * 3. Fallback to 'main'
275
+ * Cached per rootDir to avoid repeated git calls within a tick.
276
+ */
277
+ const _mainBranchCache = new Map();
278
+ function resolveMainBranch(rootDir, configuredBranch) {
279
+ const cacheKey = rootDir + ':' + (configuredBranch || '');
280
+ const cached = _mainBranchCache.get(cacheKey);
281
+ if (cached && (Date.now() - cached.ts) < 300000) return cached.branch; // 5min TTL
282
+
283
+ const gitOpts = { cwd: rootDir, encoding: 'utf8', stdio: 'pipe', timeout: 5000, windowsHide: true };
284
+
285
+ // 1. If configured branch exists, use it
286
+ if (configuredBranch) {
287
+ try {
288
+ _execSync(`git rev-parse --verify "${configuredBranch}"`, gitOpts);
289
+ _mainBranchCache.set(cacheKey, { branch: configuredBranch, ts: Date.now() });
290
+ return configuredBranch;
291
+ } catch { /* configured branch doesn't exist locally */ }
292
+ try {
293
+ _execSync(`git rev-parse --verify "origin/${configuredBranch}"`, gitOpts);
294
+ _mainBranchCache.set(cacheKey, { branch: configuredBranch, ts: Date.now() });
295
+ return configuredBranch;
296
+ } catch { /* not on remote either */ }
297
+ }
298
+
299
+ // 2. Auto-detect from remote HEAD
300
+ try {
301
+ const ref = _execSync('git symbolic-ref refs/remotes/origin/HEAD', gitOpts).trim();
302
+ const branch = ref.replace('refs/remotes/origin/', '');
303
+ if (branch) {
304
+ _mainBranchCache.set(cacheKey, { branch, ts: Date.now() });
305
+ return branch;
306
+ }
307
+ } catch { /* no remote HEAD set */ }
308
+
309
+ // 3. Fallback
310
+ const fallback = configuredBranch || 'main';
311
+ _mainBranchCache.set(cacheKey, { branch: fallback, ts: Date.now() });
312
+ return fallback;
313
+ }
314
+
270
315
  // ── Environment ─────────────────────────────────────────────────────────────
271
316
 
272
317
  function cleanChildEnv() {
@@ -651,6 +696,7 @@ module.exports = {
651
696
  writeToInbox,
652
697
  exec,
653
698
  execSilent,
699
+ resolveMainBranch,
654
700
  run,
655
701
  runFile,
656
702
  cleanChildEnv,
package/engine.js CHANGED
@@ -323,7 +323,7 @@ function spawnAgent(dispatchItem, config) {
323
323
  }
324
324
  } else {
325
325
  log('info', `Creating worktree: ${worktreePath} on branch ${branchName}`);
326
- const mainRef = sanitizeBranch(project.mainBranch || 'main');
326
+ const mainRef = sanitizeBranch(shared.resolveMainBranch(rootDir, project.mainBranch));
327
327
  try {
328
328
  runWorktreeAdd(rootDir, worktreePath, `-b "${branchName}" ${mainRef}`, _worktreeGitOpts, worktreeCreateRetries);
329
329
  } catch (e1) {
@@ -1212,7 +1212,7 @@ function materializePlansAsWorkItems(config) {
1212
1212
  const firstProject = itemsByProject.values().next().value?.project;
1213
1213
  if (!firstProject?.localPath) throw new Error('no project with localPath');
1214
1214
  const root = path.resolve(firstProject.localPath);
1215
- const mainBranch = firstProject.mainBranch || 'main';
1215
+ const mainBranch = shared.resolveMainBranch(root, firstProject.mainBranch);
1216
1216
  const branch = sanitizeBranch(plan.feature_branch);
1217
1217
  // Create branch from main (idempotent — ignores if exists)
1218
1218
  exec(`git branch "${branch}" "${mainBranch}" 2>/dev/null || true`, { cwd: root, stdio: 'pipe' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.397",
3
+ "version": "0.1.399",
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"