Refresh MCP worktree cache after create/archive (#911)
* Fix MCP worktree list cache after create/archive * Fix MCP worktree archive refresh from scoped cwd --------- Co-authored-by: zbl <zbl@zbl-M4Pro.local> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
This commit is contained in:
co-authored by
zbl
Mohamed Boudra
parent
47d236a299
commit
15cab6014c
@@ -683,8 +683,11 @@ describe("Suite E: Worktree Tools", () => {
|
|||||||
try {
|
try {
|
||||||
const created = await callToolStructured(topLevelClient, "create_worktree", {
|
const created = await callToolStructured(topLevelClient, "create_worktree", {
|
||||||
cwd: worktreeRepoCwd,
|
cwd: worktreeRepoCwd,
|
||||||
branchName,
|
target: {
|
||||||
baseBranch: "main",
|
mode: "branch-off",
|
||||||
|
newBranch: branchName,
|
||||||
|
base: "main",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
worktreePath = str(created.worktreePath);
|
worktreePath = str(created.worktreePath);
|
||||||
|
|
||||||
@@ -711,8 +714,11 @@ describe("Suite E: Worktree Tools", () => {
|
|||||||
try {
|
try {
|
||||||
const created = await callToolStructured(topLevelClient, "create_worktree", {
|
const created = await callToolStructured(topLevelClient, "create_worktree", {
|
||||||
cwd: worktreeRepoCwd,
|
cwd: worktreeRepoCwd,
|
||||||
branchName,
|
target: {
|
||||||
baseBranch: "main",
|
mode: "branch-off",
|
||||||
|
newBranch: branchName,
|
||||||
|
base: "main",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
worktreePath = str(created.worktreePath);
|
worktreePath = str(created.worktreePath);
|
||||||
|
|
||||||
@@ -731,4 +737,49 @@ describe("Suite E: Worktree Tools", () => {
|
|||||||
await archiveWorktreeIfPresent({ cwd: worktreeRepoCwd, worktreePath });
|
await archiveWorktreeIfPresent({ cwd: worktreeRepoCwd, worktreePath });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("archive_worktree succeeds when caller cwd is inside the archived worktree", async () => {
|
||||||
|
let worktreePath: string | null = null;
|
||||||
|
let worktreeAgentId: string | null = null;
|
||||||
|
let worktreeScopedClient: McpClient | null = null;
|
||||||
|
const branchName = `parity-archive-self-cwd-${Date.now()}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const created = await callToolStructured(topLevelClient, "create_worktree", {
|
||||||
|
cwd: worktreeRepoCwd,
|
||||||
|
target: {
|
||||||
|
mode: "branch-off",
|
||||||
|
newBranch: branchName,
|
||||||
|
base: "main",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
worktreePath = str(created.worktreePath);
|
||||||
|
worktreeAgentId = await createTopLevelAgent({
|
||||||
|
cwd: worktreePath,
|
||||||
|
title: "Worktree scoped parity agent",
|
||||||
|
});
|
||||||
|
worktreeScopedClient = await createMcpClient(
|
||||||
|
`http://127.0.0.1:${daemonHandle.port}/mcp/agents?callerAgentId=${encodeURIComponent(
|
||||||
|
worktreeAgentId,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const archived = await callToolStructured(worktreeScopedClient, "archive_worktree", {
|
||||||
|
worktreePath,
|
||||||
|
});
|
||||||
|
expect(archived).toEqual({ success: true });
|
||||||
|
worktreePath = null;
|
||||||
|
worktreeAgentId = null;
|
||||||
|
|
||||||
|
const listed = await callToolStructured(topLevelClient, "list_worktrees", {
|
||||||
|
cwd: worktreeRepoCwd,
|
||||||
|
});
|
||||||
|
const worktrees = recordArr(listed.worktrees);
|
||||||
|
expect(worktrees.map((worktree) => worktree.path)).not.toContain(created.worktreePath);
|
||||||
|
} finally {
|
||||||
|
await worktreeScopedClient?.close();
|
||||||
|
await archiveAgentIfPresent(worktreeAgentId);
|
||||||
|
await archiveWorktreeIfPresent({ cwd: worktreeRepoCwd, worktreePath });
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1033,6 +1033,8 @@ describe("create_agent MCP tool", () => {
|
|||||||
execFileSync("git", ["branch", "-M", "main"], { cwd: repoDir, stdio: "pipe" });
|
execFileSync("git", ["branch", "-M", "main"], { cwd: repoDir, stdio: "pipe" });
|
||||||
const workspaceGitService = {
|
const workspaceGitService = {
|
||||||
getSnapshot: vi.fn(async () => null),
|
getSnapshot: vi.fn(async () => null),
|
||||||
|
listWorktrees: vi.fn(async () => []),
|
||||||
|
resolveRepoRoot: vi.fn(async () => repoDir),
|
||||||
};
|
};
|
||||||
|
|
||||||
const server = await createAgentMcpServer({
|
const server = await createAgentMcpServer({
|
||||||
@@ -1046,7 +1048,7 @@ describe("create_agent MCP tool", () => {
|
|||||||
}),
|
}),
|
||||||
workspaceGitService: workspaceGitService as unknown as Pick<
|
workspaceGitService: workspaceGitService as unknown as Pick<
|
||||||
WorkspaceGitService,
|
WorkspaceGitService,
|
||||||
"getSnapshot" | "listWorktrees"
|
"getSnapshot" | "listWorktrees" | "resolveRepoRoot"
|
||||||
>,
|
>,
|
||||||
logger,
|
logger,
|
||||||
});
|
});
|
||||||
@@ -1059,6 +1061,10 @@ describe("create_agent MCP tool", () => {
|
|||||||
expect(response.structuredContent.branchName).toBe("tool-worktree");
|
expect(response.structuredContent.branchName).toBe("tool-worktree");
|
||||||
expect(response.structuredContent.worktreePath).toContain("tool-worktree");
|
expect(response.structuredContent.worktreePath).toContain("tool-worktree");
|
||||||
expect(workspaceGitService.getSnapshot).not.toHaveBeenCalled();
|
expect(workspaceGitService.getSnapshot).not.toHaveBeenCalled();
|
||||||
|
expect(workspaceGitService.listWorktrees).toHaveBeenCalledWith(repoDir, {
|
||||||
|
force: true,
|
||||||
|
reason: "mcp:create-worktree",
|
||||||
|
});
|
||||||
expect(setupContinuations).toEqual([undefined]);
|
expect(setupContinuations).toEqual([undefined]);
|
||||||
expect(broadcasts).toHaveLength(1);
|
expect(broadcasts).toHaveLength(1);
|
||||||
expect(broadcasts[0]).toContain("tool-worktree");
|
expect(broadcasts[0]).toContain("tool-worktree");
|
||||||
@@ -1093,6 +1099,8 @@ describe("create_agent MCP tool", () => {
|
|||||||
|
|
||||||
const workspaceGitService = {
|
const workspaceGitService = {
|
||||||
getSnapshot: vi.fn(async () => null),
|
getSnapshot: vi.fn(async () => null),
|
||||||
|
listWorktrees: vi.fn(async () => []),
|
||||||
|
resolveRepoRoot: vi.fn(async () => repoDir),
|
||||||
};
|
};
|
||||||
const archiveWorkspaceRecord = vi.fn(async () => undefined);
|
const archiveWorkspaceRecord = vi.fn(async () => undefined);
|
||||||
const emitWorkspaceUpdatesForWorkspaceIds = vi.fn(async () => undefined);
|
const emitWorkspaceUpdatesForWorkspaceIds = vi.fn(async () => undefined);
|
||||||
@@ -1106,7 +1114,7 @@ describe("create_agent MCP tool", () => {
|
|||||||
createPaseoWorktree: createPaseoWorktreeForMcpTest({ paseoHome, broadcasts: [] }),
|
createPaseoWorktree: createPaseoWorktreeForMcpTest({ paseoHome, broadcasts: [] }),
|
||||||
workspaceGitService: workspaceGitService as unknown as Pick<
|
workspaceGitService: workspaceGitService as unknown as Pick<
|
||||||
WorkspaceGitService,
|
WorkspaceGitService,
|
||||||
"getSnapshot" | "listWorktrees"
|
"getSnapshot" | "listWorktrees" | "resolveRepoRoot"
|
||||||
>,
|
>,
|
||||||
archiveWorkspaceRecord,
|
archiveWorkspaceRecord,
|
||||||
emitWorkspaceUpdatesForWorkspaceIds,
|
emitWorkspaceUpdatesForWorkspaceIds,
|
||||||
@@ -1133,6 +1141,11 @@ describe("create_agent MCP tool", () => {
|
|||||||
force: true,
|
force: true,
|
||||||
reason: "archive-worktree",
|
reason: "archive-worktree",
|
||||||
});
|
});
|
||||||
|
expect(workspaceGitService.resolveRepoRoot).toHaveBeenCalledWith(repoDir);
|
||||||
|
expect(workspaceGitService.listWorktrees).toHaveBeenCalledWith(repoDir, {
|
||||||
|
force: true,
|
||||||
|
reason: "mcp:archive-worktree",
|
||||||
|
});
|
||||||
expect(archiveWorkspaceRecord).toHaveBeenCalledWith(created.structuredContent.worktreePath);
|
expect(archiveWorkspaceRecord).toHaveBeenCalledWith(created.structuredContent.worktreePath);
|
||||||
expect(markWorkspaceArchiving).toHaveBeenCalledWith(
|
expect(markWorkspaceArchiving).toHaveBeenCalledWith(
|
||||||
[created.structuredContent.worktreePath],
|
[created.structuredContent.worktreePath],
|
||||||
|
|||||||
@@ -1858,6 +1858,10 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
resolveDefaultBranch: mcpInput.resolveDefaultBranch,
|
resolveDefaultBranch: mcpInput.resolveDefaultBranch,
|
||||||
});
|
});
|
||||||
const { worktree } = createdWorktree;
|
const { worktree } = createdWorktree;
|
||||||
|
await options.workspaceGitService?.listWorktrees?.(repoRoot, {
|
||||||
|
force: true,
|
||||||
|
reason: "mcp:create-worktree",
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [],
|
content: [],
|
||||||
@@ -1887,7 +1891,7 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
async ({ cwd, worktreePath, worktreeSlug }) => {
|
async ({ cwd, worktreePath, worktreeSlug }) => {
|
||||||
const repoRoot = resolveScopedCwd(cwd, { required: true });
|
const resolvedCwd = resolveScopedCwd(cwd, { required: true });
|
||||||
if (!worktreePath && !worktreeSlug) {
|
if (!worktreePath && !worktreeSlug) {
|
||||||
throw new Error("worktreePath or worktreeSlug is required");
|
throw new Error("worktreePath or worktreeSlug is required");
|
||||||
}
|
}
|
||||||
@@ -1912,6 +1916,7 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
if (!options.emitSessionMessage) {
|
if (!options.emitSessionMessage) {
|
||||||
throw new Error("Session message emitter is required to archive worktrees");
|
throw new Error("Session message emitter is required to archive worktrees");
|
||||||
}
|
}
|
||||||
|
const repoRoot = await options.workspaceGitService.resolveRepoRoot(resolvedCwd);
|
||||||
|
|
||||||
const targetPath =
|
const targetPath =
|
||||||
worktreePath ??
|
worktreePath ??
|
||||||
@@ -1948,6 +1953,10 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
requestId: "mcp:archive_worktree",
|
requestId: "mcp:archive_worktree",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
await options.workspaceGitService.listWorktrees(repoRoot, {
|
||||||
|
force: true,
|
||||||
|
reason: "mcp:archive-worktree",
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [],
|
content: [],
|
||||||
|
|||||||
Reference in New Issue
Block a user