@caseyharalson/orrery 0.14.0 → 0.14.2

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.
@@ -205,7 +205,7 @@ module.exports = function registerResumeCommand(program) {
205
205
  console.log("Resuming orchestration...\n");
206
206
  await orchestrate({
207
207
  resume: true,
208
- plan: options.plan,
208
+ plan: planFile,
209
209
  onComplete: options.onComplete
210
210
  });
211
211
  return;
@@ -280,7 +280,7 @@ module.exports = function registerResumeCommand(program) {
280
280
  console.log("\nResuming orchestration...\n");
281
281
  await orchestrate({
282
282
  resume: true,
283
- plan: options.plan,
283
+ plan: planFile,
284
284
  onComplete: options.onComplete
285
285
  });
286
286
  });
@@ -24,20 +24,26 @@ function getAgentPriority() {
24
24
  function getInstalledAgentPriority() {
25
25
  const requestedAgents = getAgentPriority();
26
26
  const installedAgents = detectInstalledAgents();
27
+ const isExplicit = !!(
28
+ process.env.ORRERY_AGENT_PRIORITY &&
29
+ process.env.ORRERY_AGENT_PRIORITY.trim()
30
+ );
27
31
 
28
32
  const filteredAgents = requestedAgents.filter((agent) =>
29
33
  installedAgents.includes(agent)
30
34
  );
31
35
 
32
- // Warn about agents in priority that aren't installed
33
- const skippedAgents = requestedAgents.filter(
34
- (agent) => !installedAgents.includes(agent)
35
- );
36
- if (skippedAgents.length > 0) {
37
- const missingDirs = skippedAgents.map((a) => `~/.${a}`).join(", ");
38
- console.warn(
39
- `[config] Skipping unconfigured agents: ${skippedAgents.join(", ")} (missing: ${missingDirs})`
36
+ // Only warn about missing agents when the user explicitly set ORRERY_AGENT_PRIORITY
37
+ if (isExplicit) {
38
+ const skippedAgents = requestedAgents.filter(
39
+ (agent) => !installedAgents.includes(agent)
40
40
  );
41
+ if (skippedAgents.length > 0) {
42
+ const missingDirs = skippedAgents.map((a) => `~/.${a}`).join(", ");
43
+ console.warn(
44
+ `[config] Skipping unconfigured agents: ${skippedAgents.join(", ")} (missing: ${missingDirs})`
45
+ );
46
+ }
41
47
  }
42
48
 
43
49
  if (filteredAgents.length === 0) {
@@ -380,6 +380,26 @@ async function orchestrate(options = {}) {
380
380
  );
381
381
  }
382
382
 
383
+ // Resume mode: find and continue the plan for the current branch
384
+ if (normalizedOptions.resume) {
385
+ const sourceBranch = getCurrentBranch(REPO_ROOT);
386
+ if (hasUncommittedChanges(REPO_ROOT)) {
387
+ console.error(
388
+ "Error: Uncommitted changes detected. Please commit or stash before resuming."
389
+ );
390
+ process.exit(1);
391
+ }
392
+ await handleResumeMode(
393
+ plansDir,
394
+ completedDir,
395
+ reportsDir,
396
+ sourceBranch,
397
+ normalizedOptions.plan,
398
+ normalizedOptions.onComplete
399
+ );
400
+ return;
401
+ }
402
+
383
403
  // Filter out plans that are already dispatched (have work_branch set)
384
404
  const dispatchedPlans = [];
385
405
 
@@ -432,19 +452,6 @@ async function orchestrate(options = {}) {
432
452
  process.exit(1);
433
453
  }
434
454
 
435
- // Resume mode: find and continue the plan for the current branch
436
- if (normalizedOptions.resume) {
437
- await handleResumeMode(
438
- plansDir,
439
- completedDir,
440
- reportsDir,
441
- sourceBranch,
442
- normalizedOptions.plan,
443
- normalizedOptions.onComplete
444
- );
445
- return;
446
- }
447
-
448
455
  console.log(`Found ${planFiles.length} plan(s) to process:\n`);
449
456
  for (const pf of planFiles) {
450
457
  console.log(` - ${path.basename(pf)}`);
@@ -1373,7 +1380,7 @@ async function processPlan(
1373
1380
  tracker
1374
1381
  );
1375
1382
  plan = loadPlan(planFile);
1376
- await mergeWorktreeCommits(results, ctx.workingDir);
1383
+ await mergeWorktreeCommits(results, ctx.workingDir, ctx.mainRepoRoot);
1377
1384
  } else {
1378
1385
  // Serial mode: wait for one
1379
1386
  const { stepIds, parsedResults } = await waitForAgentCompletion(
@@ -1476,7 +1483,7 @@ async function processPlan(
1476
1483
  tracker
1477
1484
  );
1478
1485
  plan = loadPlan(planFile);
1479
- await mergeWorktreeCommits(results, ctx.workingDir);
1486
+ await mergeWorktreeCommits(results, ctx.workingDir, ctx.mainRepoRoot);
1480
1487
  } else {
1481
1488
  // Serial execution: wait for one agent at a time
1482
1489
  const { stepIds, parsedResults } = await waitForAgentCompletion(
@@ -1888,9 +1895,11 @@ async function waitForAllParallelAgents(
1888
1895
  /**
1889
1896
  * Merge commits from worktrees back to main repository
1890
1897
  * @param {Object[]} completedAgents - Array of completed agent results
1891
- * @param {string} repoRoot - Path to main repository
1898
+ * @param {string} repoRoot - Path to working directory (plan worktree) for cherry-pick
1899
+ * @param {string} [mainRepoRoot] - Path to main repository where step worktree branches live
1892
1900
  */
1893
- async function mergeWorktreeCommits(completedAgents, repoRoot) {
1901
+ async function mergeWorktreeCommits(completedAgents, repoRoot, mainRepoRoot) {
1902
+ const branchLookupRoot = mainRepoRoot || repoRoot;
1894
1903
  const allCommits = [];
1895
1904
  const worktreesToClean = [];
1896
1905
 
@@ -1904,8 +1913,24 @@ async function mergeWorktreeCommits(completedAgents, repoRoot) {
1904
1913
  worktreesToClean.push({ path: worktreePath, branch: branchName });
1905
1914
 
1906
1915
  try {
1907
- // Get commits made in worktree branch
1908
- const commits = getCommitRange("HEAD", branchName, repoRoot);
1916
+ // Commit any uncommitted changes the agent left in the step worktree
1917
+ if (fs.existsSync(worktreePath) && hasUncommittedChanges(worktreePath)) {
1918
+ const stepDesc = agent.stepIds ? agent.stepIds.join(", ") : branchName;
1919
+ const commitSha = commit(
1920
+ `feat: complete step(s) ${stepDesc}`,
1921
+ [],
1922
+ worktreePath
1923
+ );
1924
+ if (commitSha) {
1925
+ console.log(
1926
+ `Committed agent changes in worktree ${branchName} (${commitSha.slice(0, 7)})`
1927
+ );
1928
+ }
1929
+ }
1930
+
1931
+ // Get commits made in worktree branch — branches live in the main repo,
1932
+ // not in the plan worktree, so use branchLookupRoot for the range query
1933
+ const commits = getCommitRange("HEAD", branchName, branchLookupRoot);
1909
1934
  if (commits.length > 0) {
1910
1935
  console.log(
1911
1936
  `Found ${commits.length} commit(s) in worktree ${branchName}`
@@ -1949,11 +1974,11 @@ async function mergeWorktreeCommits(completedAgents, repoRoot) {
1949
1974
  }
1950
1975
  }
1951
1976
 
1952
- // Clean up worktrees
1977
+ // Clean up worktrees — these were created in the main repo, not the plan worktree
1953
1978
  for (const worktree of worktreesToClean) {
1954
1979
  try {
1955
- removeWorktree(worktree.path, repoRoot);
1956
- deleteBranch(worktree.branch, repoRoot, true);
1980
+ removeWorktree(worktree.path, branchLookupRoot);
1981
+ deleteBranch(worktree.branch, branchLookupRoot, true);
1957
1982
  console.log(`Cleaned up worktree: ${worktree.branch}`);
1958
1983
  } catch (err) {
1959
1984
  console.error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caseyharalson/orrery",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "Workflow planning and orchestration CLI for AI agents",
5
5
  "license": "MIT",
6
6
  "author": "Casey Haralson",
@@ -34,7 +34,8 @@
34
34
  "lint": "eslint .",
35
35
  "prepublishOnly": "npm run lint && npm run format:check && npm test",
36
36
  "release:prepare": "node scripts/prepare-release.js",
37
- "test": "node --test",
37
+ "test": "node --test test/cli.test.js test/cli/*.test.js test/helpers/*.js test/orchestration/*.test.js test/utils/*.test.js",
38
+ "test:integration": "node --test --test-timeout=600000 test/integration/scenarios/*.test.js",
38
39
  "validate": "npm run format:check && npm run lint && npm test"
39
40
  },
40
41
  "files": [