Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions apps/ade-cli/src/adeRpcServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -700,14 +700,18 @@ const TOOL_SPECS: ToolSpec[] = [
},
{
name: "git_checkout_branch",
description: "Checkout an existing branch in a lane checkout.",
description: "Switch a lane checkout to an existing branch or create a new branch in that lane.",
inputSchema: {
type: "object",
required: ["branchName"],
additionalProperties: false,
properties: {
laneId: { type: "string", minLength: 1 },
branchName: { type: "string", minLength: 1 }
branchName: { type: "string", minLength: 1 },
mode: { type: "string", enum: ["existing", "create"] },
startPoint: { type: "string", minLength: 1 },
baseRef: { type: "string", minLength: 1 },
acknowledgeActiveWork: { type: "boolean" }
}
Comment thread
arul28 marked this conversation as resolved.
}
},
Expand Down Expand Up @@ -5258,7 +5262,18 @@ async function runTool(args: {
if (name === "git_checkout_branch") {
const laneId = requireLaneIdForTool(runtime, session, toolArgs, "git_checkout_branch");
const branchName = assertNonEmptyString(toolArgs.branchName, "branchName");
const action = await runtime.gitService.checkoutBranch({ laneId, branchName });
const mode = typeof toolArgs.mode === "string" ? toolArgs.mode : undefined;
const startPoint = typeof toolArgs.startPoint === "string" ? toolArgs.startPoint : undefined;
const baseRef = typeof toolArgs.baseRef === "string" ? toolArgs.baseRef : undefined;
const acknowledgeActiveWork = typeof toolArgs.acknowledgeActiveWork === "boolean" ? toolArgs.acknowledgeActiveWork : undefined;
const action = await runtime.gitService.checkoutBranch({
laneId,
branchName,
mode: mode === "create" ? "create" : "existing",
startPoint,
baseRef,
acknowledgeActiveWork,
});
Comment thread
arul28 marked this conversation as resolved.
Outdated
return { laneId, branchName, action };
}

Expand Down
65 changes: 65 additions & 0 deletions apps/ade-cli/src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,71 @@ describe("ADE CLI", () => {
});
});

it("maps `git checkout <branch>` to git_checkout_branch with mode=existing by default", () => {
const plan = buildCliPlan(["git", "checkout", "feature/foo", "--lane", "lane-1"]);
expect(plan.kind).toBe("execute");
if (plan.kind !== "execute") return;

expect(plan.steps[0]?.params).toEqual({
name: "git_checkout_branch",
arguments: {
laneId: "lane-1",
branchName: "feature/foo",
mode: "existing",
acknowledgeActiveWork: false,
},
});
});

it("maps `git checkout --create` to mode=create with optional --from/--base", () => {
const plan = buildCliPlan([
"git", "checkout",
"feature/new",
"--lane", "lane-1",
"--create",
"--from", "main",
"--base", "main",
"--ack-active-work",
]);
expect(plan.kind).toBe("execute");
if (plan.kind !== "execute") return;

expect(plan.steps[0]?.params).toEqual({
name: "git_checkout_branch",
arguments: {
laneId: "lane-1",
branchName: "feature/new",
mode: "create",
startPoint: "main",
baseRef: "main",
acknowledgeActiveWork: true,
},
});
});

it("accepts the `-b` short flag as an alias for --create", () => {
const plan = buildCliPlan(["git", "checkout", "topic-1", "--lane", "lane-1", "-b"]);
expect(plan.kind).toBe("execute");
if (plan.kind !== "execute") return;
const args = (plan.steps[0]?.params as { arguments: Record<string, unknown> }).arguments;
expect(args.mode).toBe("create");
expect(args.branchName).toBe("topic-1");
});

it("omits startPoint and baseRef from the call when not supplied", () => {
const plan = buildCliPlan(["git", "checkout", "feature/x", "--lane", "lane-1"]);
expect(plan.kind).toBe("execute");
if (plan.kind !== "execute") return;
const args = (plan.steps[0]?.params as { arguments: Record<string, unknown> }).arguments;
expect(args).not.toHaveProperty("startPoint");
expect(args).not.toHaveProperty("baseRef");
});

it("rejects `git checkout` without a branch name", () => {
expect(() => buildCliPlan(["git", "checkout", "--lane", "lane-1"]))
.toThrow(/branchName/);
});

it("shows command help from subcommand help flags", () => {
const prsHelp = buildCliPlan(["prs", "create", "--help"]);
expect(prsHelp.kind).toBe("help");
Expand Down
16 changes: 15 additions & 1 deletion apps/ade-cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,21 @@ function buildGitPlan(args: string[]): CliPlan {
if (sub === "branches" || sub === "branch") return { kind: "execute", label: "git branches", steps: [actionCallStep("result", "git_list_branches", withLane())] };
if (sub === "checkout") {
const branchName = requireValue(readValue(args, ["--branch", "--branch-name"]) ?? firstPositional(args), "branchName");
return { kind: "execute", label: "git checkout", steps: [actionCallStep("result", "git_checkout_branch", withLane({ branchName }))] };
const create = readFlag(args, ["--create", "-b"]);
const startPoint = readValue(args, ["--start-point", "--from"]);
const baseRef = readValue(args, ["--base", "--base-ref"]);
const acknowledgeActiveWork = readFlag(args, ["--ack-active-work"]);
Comment thread
arul28 marked this conversation as resolved.
return {
kind: "execute",
label: "git checkout",
steps: [actionCallStep("result", "git_checkout_branch", withLane({
branchName,
mode: create ? "create" : "existing",
...(startPoint ? { startPoint } : {}),
...(baseRef ? { baseRef } : {}),
acknowledgeActiveWork,
}))]
};
}
if (sub === "conflicts") return { kind: "execute", label: "git conflicts", steps: [actionCallStep("result", "get_lane_conflict_state", withLane())] };
if (sub === "rebase") {
Expand Down
18 changes: 16 additions & 2 deletions apps/desktop/src/main/services/ai/tools/ctoOperatorTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2649,8 +2649,22 @@ export function createCtoOperatorTools(deps: CtoOperatorToolDeps): Record<string

tools.gitCheckoutBranch = tool({
description: "Switch to or create a git branch in a lane.",
inputSchema: z.object({ laneId: z.string().optional(), branch: z.string().min(1), create: z.boolean().optional().default(false) }),
execute: ({ laneId, branch, create }) => gitGuard(() => deps.gitService!.checkoutBranch({ laneId: resolveLaneId(laneId), branch, create })),
inputSchema: z.object({
laneId: z.string().optional(),
branch: z.string().min(1),
create: z.boolean().optional().default(false),
startPoint: z.string().optional(),
baseRef: z.string().optional(),
acknowledgeActiveWork: z.boolean().optional().default(false),
}),
execute: ({ laneId, branch, create, startPoint, baseRef, acknowledgeActiveWork }) => gitGuard(() => deps.gitService!.checkoutBranch({
laneId: resolveLaneId(laneId),
branchName: branch,
mode: create ? "create" : "existing",
startPoint,
baseRef,
acknowledgeActiveWork,
})),
});

tools.gitStashPush = tool({
Expand Down
Loading
Loading