@yemi33/minions 0.1.1776 → 0.1.1777

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,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1777 (2026-05-07)
4
+
5
+ ### Fixes
6
+ - tighten worktreeRoot validation everywhere worktrees are created
7
+
3
8
  ## 0.1.1776 (2026-05-07)
4
9
 
5
10
  ### Fixes
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "runtime": "copilot",
3
3
  "models": null,
4
- "cachedAt": "2026-05-07T23:06:07.639Z"
4
+ "cachedAt": "2026-05-07T23:17:20.330Z"
5
5
  }
@@ -1745,6 +1745,17 @@ async function rebaseBranchOntoMain(pr, project, config) {
1745
1745
  const tmpWt = path.join(wtRoot, `rebase-${shared.sanitizeBranch(branch)}-${Date.now()}`).replace(/\\/g, '/');
1746
1746
  const _gitOpts = { cwd: root, timeout: 30000, windowsHide: true };
1747
1747
 
1748
+ // Refuse to create a worktree nested in the project root — would cause
1749
+ // glob/grep tools running with cwd=root to match both copies of every file
1750
+ // and produce mirror writes. Misconfigured engine.worktreeRoot is the only
1751
+ // way this lands inside root; the assert throws so the caller can recover.
1752
+ try {
1753
+ shared.assertWorktreeOutsideProject(tmpWt, root);
1754
+ } catch (err) {
1755
+ log('warn', `Post-merge rebase: refusing nested worktree path — ${err.message}`);
1756
+ return { success: false, error: err.message };
1757
+ }
1758
+
1748
1759
  try {
1749
1760
  await execAsync(`git fetch origin "${mainBranch}" "${branch}"`, _gitOpts);
1750
1761
  try {
@@ -238,8 +238,13 @@ function runPreflight(opts = {}) {
238
238
  // us the config. checkOrExit() / cli start() / doctor() pass it; legacy
239
239
  // callers don't, in which case we skip silently.
240
240
  if (opts && opts.config && typeof opts.config === 'object') {
241
+ // Hoisted: `shared` is referenced by every check block below, including
242
+ // workSources warnings and the worktreeRoot check. Previously declared
243
+ // inside the first inner try, which left later blocks reading an
244
+ // undefined identifier (ReferenceError silently caught by the wrapping
245
+ // try/catch).
246
+ const shared = require('./shared');
241
247
  try {
242
- const shared = require('./shared');
243
248
  let runtimeNames = [];
244
249
  try { runtimeNames = require('./runtimes').listRuntimes(); }
245
250
  catch { /* registry may be missing during partial installs */ }
@@ -266,6 +271,32 @@ function runPreflight(opts = {}) {
266
271
  results.push({ name: `Project config (${w.id})`, ok: 'warn', message: w.message });
267
272
  }
268
273
  } catch { /* defensive */ }
274
+
275
+ // 5. worktreeRoot config check — for every linked project, verify that the
276
+ // configured engine.worktreeRoot resolves OUTSIDE the project's
277
+ // localPath. A nested worktreeRoot causes glob/grep to match both
278
+ // copies of every file, producing silent mirror writes (the W-cc-doc-
279
+ // chat-continuity leak class). Hard-fail at preflight so the operator
280
+ // sees it before any agent dispatch — the runtime guard in spawnAgent
281
+ // is the second line of defense.
282
+ try {
283
+ const path = require('path');
284
+ const projects = shared.getProjects(opts.config) || [];
285
+ const wtRoot = opts.config?.engine?.worktreeRoot || shared.ENGINE_DEFAULTS.worktreeRoot;
286
+ for (const project of projects) {
287
+ if (!project || !project.localPath) continue;
288
+ const root = path.resolve(project.localPath);
289
+ const resolved = path.resolve(root, wtRoot);
290
+ if (shared.isPathInsideOrEqual(resolved, root)) {
291
+ results.push({
292
+ name: `worktreeRoot (${project.name || root})`,
293
+ ok: false,
294
+ message: `engine.worktreeRoot "${wtRoot}" resolves to "${resolved}" — INSIDE project root "${root}". This causes glob/grep to match duplicate files and produces mirror writes. Set engine.worktreeRoot to a sibling path (default: "../worktrees").`,
295
+ });
296
+ allOk = false;
297
+ }
298
+ }
299
+ } catch { /* defensive — preflight must never throw */ }
269
300
  }
270
301
 
271
302
  return { passed: allOk, results };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1776",
3
+ "version": "0.1.1777",
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"