feat(HAI-034): use humanized worktree names instead of task-ID-based directories
- Add worktree-names module with adjective-animal name generator (generateWorktreeName) - Update executor to assign humanized random worktree names and support dependency worktree reuse - Remove task-ID fallback paths from merger and store; add shared worktree cleanup via findWorktreeUser - Remove worktree pool module and related scheduler/integration tests - Update dashboard worktree label tests and add JSDoc documentation for worktree naming
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import { join } from "node:path";
|
||||
import { existsSync } from "node:fs";
|
||||
import type { TaskStore, Task, MergeResult } from "@hai/core";
|
||||
import { createHaiAgent } from "./pi.js";
|
||||
@@ -101,7 +100,7 @@ export async function aiMergeTask(
|
||||
}
|
||||
|
||||
const branch = `hai/${taskId.toLowerCase()}`;
|
||||
const worktreePath = task.worktree || join(rootDir, ".worktrees", taskId);
|
||||
const worktreePath = task.worktree;
|
||||
const result: MergeResult = {
|
||||
task,
|
||||
branch,
|
||||
@@ -110,6 +109,10 @@ export async function aiMergeTask(
|
||||
branchDeleted: false,
|
||||
};
|
||||
|
||||
if (!worktreePath) {
|
||||
console.warn(`[merger] ${taskId}: no worktree path set — skipping worktree cleanup`);
|
||||
}
|
||||
|
||||
// 2. Check branch exists
|
||||
try {
|
||||
execSync(`git rev-parse --verify "${branch}"`, {
|
||||
@@ -221,17 +224,23 @@ export async function aiMergeTask(
|
||||
session.dispose();
|
||||
}
|
||||
|
||||
// 7. Clean up worktree — release to pool if recycling is enabled
|
||||
if (existsSync(worktreePath)) {
|
||||
const settings = await store.getSettings();
|
||||
if (options.pool && settings.recycleWorktrees) {
|
||||
// Detach HEAD so the task branch can be deleted in step 8
|
||||
try {
|
||||
execSync("git checkout --detach", { cwd: worktreePath, stdio: "pipe" });
|
||||
} catch { /* non-fatal — prepareForTask will reset HEAD on next acquire */ }
|
||||
options.pool.release(worktreePath);
|
||||
// 7. Delete branch (always per-task, regardless of worktree sharing)
|
||||
try {
|
||||
execSync(`git branch -d "${branch}"`, { cwd: rootDir, stdio: "pipe" });
|
||||
result.branchDeleted = true;
|
||||
} catch {
|
||||
try {
|
||||
execSync(`git branch -D "${branch}"`, { cwd: rootDir, stdio: "pipe" });
|
||||
result.branchDeleted = true;
|
||||
} catch { /* non-fatal */ }
|
||||
}
|
||||
|
||||
// 8. Clean up worktree — only if no other non-done task still references it
|
||||
if (worktreePath && existsSync(worktreePath)) {
|
||||
const otherUser = await findWorktreeUser(store, worktreePath, taskId);
|
||||
if (otherUser) {
|
||||
console.log(`[merger] Worktree retained — still needed by ${otherUser}`);
|
||||
result.worktreeRemoved = false;
|
||||
console.log(`[merger] Worktree returned to pool: ${worktreePath}`);
|
||||
} else {
|
||||
try {
|
||||
execSync(`git worktree remove "${worktreePath}" --force`, {
|
||||
@@ -243,17 +252,6 @@ export async function aiMergeTask(
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Delete branch
|
||||
try {
|
||||
execSync(`git branch -d "${branch}"`, { cwd: rootDir, stdio: "pipe" });
|
||||
result.branchDeleted = true;
|
||||
} catch {
|
||||
try {
|
||||
execSync(`git branch -D "${branch}"`, { cwd: rootDir, stdio: "pipe" });
|
||||
result.branchDeleted = true;
|
||||
} catch { /* non-fatal */ }
|
||||
}
|
||||
|
||||
// 9. Move task to done
|
||||
await completeTask(store, taskId, result);
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user