【从0到1构建一个ClaudeAgent】协作-Worktree+任务隔离
2026/6/25 19:01:01 网站建设 项目流程

任务板管 "做什么" 但不管 "在哪做"。解法: 给每个任务一个独立的 git worktree 目录, 用任务 ID 把两边关联起来。"各干各的目录, 互不干扰",任务管目标,,worktree 管目录,按 ID 绑定。

Java实现代码

java

public class WorktreeTaskIsolationSystem { // --- 新增配置 --- private static final Path REPO_ROOT = detectRepoRoot(WORKDIR); // Git仓库根目录 private static final Path TASKS_DIR = REPO_ROOT.resolve(".tasks"); private static final Path WORKTREES_DIR = REPO_ROOT.resolve(".worktrees"); private static final Path EVENTS_LOG_PATH = WORKTREES_DIR.resolve("events.jsonl"); // --- 新增:Git 仓库检测 --- private static Path detectRepoRoot(Path cwd) { try { ProcessBuilder pb = new ProcessBuilder("git", "rev-parse", "--show-toplevel"); pb.directory(cwd.toFile()); Process process = pb.start(); boolean finished = process.waitFor(10, TimeUnit.SECONDS); if (!finished) { return cwd; } String output = new String(process.getInputStream().readAllBytes()).trim(); Path root = Paths.get(output); return Files.exists(root) ? root : cwd; // 自动检测:自动发现Git仓库根目录 // 向后兼容:如果不是Git仓库,使用当前目录 } catch (Exception e) { return cwd; } } // --- 新增:事件总线(EventBus)--- static class EventBus { private final Path eventLogPath; public EventBus(Path eventLogPath) { this.eventLogPath = eventLogPath; try { Files.createDirectories(eventLogPath.getParent()); if (!Files.exists(eventLogPath)) { Files.writeString(eventLogPath, ""); } } catch (IOException e) { throw new RuntimeException("Failed to create event bus", e); } } /** * 发出事件 */ public void emit(String event, Map<String, Object> task, Map<String, Object> worktree, String error) { Map<String, Object> payload = new LinkedHashMap<>(); payload.put("event", event); payload.put("ts", System.currentTimeMillis() / 1000.0); payload.put("task", task != null ? task : Map.of()); payload.put("worktree", worktree != null ? worktree : Map.of()); if (error != null) { payload.put("error", error); } // 结构化事件:事件类型、时间戳、关联数据 // 错误跟踪:支持记录操作失败信息 try { String jsonLine = gson.toJson(payload) + "\n"; Files.writeString(eventLogPath, jsonLine, StandardOpenOption.CREATE, StandardOpenOption.APPEND); // JSONL格式:每行一个事件,便于流式处理 // 追加写入:支持长时间运行的事件记录 } catch (IOException e) { System.err.println("Failed to emit event: " + e.getMessage()); } } /** * 列出最近事件 */ public String listRecent(int limit) { int n = Math.max(1, Math.min(limit, 200)); try { List<String> lines = Files.readAllLines(eventLogPath); List<Map<String, Object>> events = new ArrayList<>(); int start = Math.max(0, lines.size() - n); for (int i = start; i < lines.size(); i++) { try { Type type = new TypeToken<Map<String, Object>>(){}.getType(); Map<String, Object> event = gson.fromJson(lines.get(i), type); events.add(event); } catch (Exception e) { Map<String, Object> errorEvent = new HashMap<>(); errorEvent.put("event", "parse_error"); errorEvent.put("raw", lines.get(i)); events.add(errorEvent); // 容错处理:即使解析失败也记录原始数据 } } return gson.toJson(events); } catch (IOException e) { return "[]"; } } } // 初始化事件总线 private static final EventBus EVENTS = new EventBus(EVENTS_LOG_PATH); // --- 新增:任务管理器(支持工作树绑定)--- static class TaskManager { private final Path tasksDir; private int nextId = 1; public TaskManager(Path tasksDir) { this.tasksDir = tasksDir; try { Files.createDirectories(tasksDir); this.nextId = getMaxId() + 1; } catch (IOException e) { throw new RuntimeException("Failed to initialize task manager", e); } } /** * 绑定任务到工作树 */ public String bindWorktree(int taskId, String worktree, String owner) throws IOException { Map<String, Object> task = loadTask(taskId); task.put("worktree", worktree); // 任务-工作树关联:建立任务和工作树的双向链接 if (owner != null && !owner.isEmpty()) { task.put("owner", owner); } if ("pending".equals(task.get("status"))) { task.put("status", "in_progress"); // 状态自动转换:绑定工作树时自动开始任务 } task.put("updated_at", System.currentTimeMillis() / 1000.0); saveTask(task); return gson.toJson(task); } /** * 解绑工作树 */ public String unbindWorktree(int taskId) throws IOException { Map<String, Object> task = loadTask(taskId); task.put("worktree", ""); task.put("updated_at", System.currentTimeMillis() / 1000.0); saveTask(task); return gson.toJson(task); // 解绑机制:支持任务和工作树的解耦 } /** * 列出所有任务 */ public String listAllTasks() throws IOException { // ... 列出任务逻辑,包含工作树绑定信息 sb.append(String.format("%s #%d: %s%s%s\n", marker, id, subject, ownerStr, worktreeStr)); // 可视化展示:显示任务状态、所有者、工作树绑定 } } // 初始化任务管理器 private static final TaskManager TASKS = new TaskManager(TASKS_DIR); // --- 新增:工作树管理器(WorktreeManager)--- static class WorktreeManager { private final Path repoRoot; private final TaskManager taskManager; private final EventBus eventBus; private final Path worktreesDir; private final Path indexPath; private final boolean gitAvailable; public WorktreeManager(Path repoRoot, TaskManager taskManager, EventBus eventBus) { this.repoRoot = repoRoot; this.taskManager = taskManager; this.eventBus = eventBus; this.worktreesDir = repoRoot.resolve(".worktrees"); this.indexPath = worktreesDir.resolve("index.json"); // 集成架构:工作树管理器与任务管理器、事件总线集成 try { Files.createDirectories(worktreesDir); if (!Files.exists(indexPath)) { Map<String, Object> index = new HashMap<>(); index.put("worktrees", new ArrayList<Map<String, Object>>()); Files.writeString(indexPath, gson.toJson(index)); } } catch (IOException e) { throw new RuntimeException("Failed to initialize worktree manager", e); } this.gitAvailable = isGitRepo(); } /** * 检查是否是Git仓库 */ private boolean isGitRepo() { try { ProcessBuilder pb = new ProcessBuilder("git", "rev-parse", "--is-inside-work-tree"); pb.directory(repoRoot.toFile()); Process process = pb.start(); return process.exitValue() == 0; } catch (Exception e) { return false; } } /** * 执行Git命令 */ private String runGit(List<String> args) throws Exception { if (!gitAvailable) { throw new RuntimeException("Not in a git repository. worktree tools require git."); } ProcessBuilder pb = new ProcessBuilder("git"); pb.command().addAll(args); pb.directory(repoRoot.toFile()); Process process = pb.start(); boolean finished = process.waitFor(120, TimeUnit.SECONDS); if (!finished) { process.destroy(); throw new RuntimeException("git command timeout"); } int exitCode = process.exitValue(); if (exitCode != 0) { String error = new String(process.getErrorStream().readAllBytes()).trim(); String output = new String(process.getInputStream().readAllBytes()).trim(); String message = error.isEmpty() ? output : error; if (message.isEmpty()) { message = "git " + String.join(" ", args) + " failed"; } throw new RuntimeException(message); } String output = new String(process.getInputStream().readAllBytes()).trim(); String error = new String(process.getErrorStream().readAllBytes()).trim(); String result = output + (error.isEmpty() ? "" : "\n" + error); return result.isEmpty() ? "(no output)" : result; } /** * 加载索引文件 */ @SuppressWarnings("unchecked") private Map<String, Object> loadIndex() throws IOException { String content = Files.readString(indexPath); Type type = new TypeToken<Map<String, Object>>(){}.getType(); return gson.fromJson(content, type); } /** * 保存索引文件 */ private void saveIndex(Map<String, Object> index) throws IOException { Files.writeString(indexPath, gson.toJson(index)); } /** * 验证工作树名称 */

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询