Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions apps/ade-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@ ade lanes list --text
ade lanes create "fix-checkout-flow" --parent main
ade lanes create "lin-123" --linear-issue-json '{"id":"...","identifier":"LIN-123","title":"...","projectId":"...","projectSlug":"...","teamId":"...","teamKey":"...","stateId":"...","stateName":"Todo","stateType":"unstarted","priority":2,"priorityLabel":"high","labels":[],"assigneeId":null,"assigneeName":null,"createdAt":"...","updatedAt":"..."}'
ade lanes reparent lane-child --parent lane-parent --stack-base-branch main
ade lanes create-from-linear --issue-id ENG-431 --start-chat --provider codex --model <model>
ade lanes batch-create-from-linear --linear-issues-json '[{"id":"...","identifier":"ENG-431"},{"id":"...","identifier":"ENG-440"}]'
ade chat attach-linear-issue <session> --issue-id ENG-431
ade chat create --from-linear-issue ENG-431
ade linear attach --this-session --issue-id ENG-431 # attach to the current CLI session ($ADE_CHAT_SESSION_ID)
ade linear comment "Pushed a fix; CI running" # write back to the session's attached issue over the daemon bridge
ade linear set-state ENG-431 <state-id>
ade --role cto linear quick-view --text
ade --role cto linear search-issues --query "auth" --state-type started,unstarted --first 50
ade --role cto linear issue-comments --issue-id <linear-issue-uuid>
Expand Down
83 changes: 83 additions & 0 deletions apps/ade-cli/src/adeRpcServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,21 @@ function createRuntime() {
name: name ?? "Imported lane",
branchRef,
})),
linkLinearIssues: vi.fn((args: { laneId: string; issues: unknown[] }) =>
args.issues.map((issue, index) => ({ id: `link-${index}`, laneId: args.laneId, issue })),
),
unlinkLinearIssues: vi.fn(() => true),
attachLinearIssueToSession: vi.fn((args: { chatSessionId: string; issues: unknown[] }) =>
args.issues.map((issue, index) => ({
id: `session-link-${index}`,
chatSessionId: args.chatSessionId,
issue,
})),
),
detachLinearIssueFromSession: vi.fn(() => true),
listLinearIssuesForSession: vi.fn((args: { chatSessionId: string }) => [
{ id: "session-link-0", chatSessionId: args.chatSessionId, issue: { id: "issue-1", identifier: "ENG-1" } },
]),
delete: vi.fn(async () => {})
},
sessionService: {
Expand Down Expand Up @@ -2868,6 +2883,74 @@ describe("adeRpcServer", () => {

});

it("routes Linear attach/detach/list and the issue write-bridge through run_ade_action", async () => {
const fixture = createRuntime();
const handler = createAdeRpcRequestHandler({ runtime: fixture.runtime, serverVersion: "test" });
await initialize(handler, { callerId: "agent-1", role: "agent" });

// Session-scoped attach: issues array keyed by chatSessionId.
const attach = await callTool(handler, "run_ade_action", {
domain: "lane",
action: "attachLinearIssueToSession",
args: { chatSessionId: "session-9", issues: [{ id: "issue-1", identifier: "ENG-431" }] },
});
expect(attach?.isError).toBeUndefined();
expect(fixture.runtime.laneService.attachLinearIssueToSession).toHaveBeenCalledWith({
chatSessionId: "session-9",
issues: [{ id: "issue-1", identifier: "ENG-431" }],
});

// Detach: chatSessionId + optional issueId.
const detach = await callTool(handler, "run_ade_action", {
domain: "lane",
action: "detachLinearIssueFromSession",
args: { chatSessionId: "session-9", issueId: "ENG-431" },
});
expect(detach?.isError).toBeUndefined();
expect(fixture.runtime.laneService.detachLinearIssueFromSession).toHaveBeenCalledWith({
chatSessionId: "session-9",
issueId: "ENG-431",
});

// List: object arg.
const list = await callTool(handler, "run_ade_action", {
domain: "lane",
action: "listLinearIssuesForSession",
args: { chatSessionId: "session-9" },
});
expect(list?.isError).toBeUndefined();
expect(fixture.runtime.laneService.listLinearIssuesForSession).toHaveBeenCalledWith({
chatSessionId: "session-9",
});
expect(list.structuredContent.result).toHaveLength(1);

// Lane-scoped unlink (issueId omitted = remove all non-primary links).
const unlink = await callTool(handler, "run_ade_action", {
domain: "lane",
action: "unlinkLinearIssues",
args: { laneId: "lane-1" },
});
expect(unlink?.isError).toBeUndefined();
expect(fixture.runtime.laneService.unlinkLinearIssues).toHaveBeenCalledWith({ laneId: "lane-1" });

// Write-bridge: createComment + updateIssueState (positional).
const comment = await callTool(handler, "run_ade_action", {
domain: "linear_issue_tracker",
action: "createComment",
argsList: ["ENG-431", "All green"],
});
expect(comment?.isError).toBeUndefined();
expect(fixture.runtime.linearIssueTracker.createComment).toHaveBeenCalledWith("ENG-431", "All green");

const setState = await callTool(handler, "run_ade_action", {
domain: "linear_issue_tracker",
action: "updateIssueState",
argsList: ["ENG-431", "state-done"],
});
expect(setState?.isError).toBeUndefined();
expect(fixture.runtime.linearIssueTracker.updateIssueState).toHaveBeenCalledWith("ENG-431", "state-done");
});

it("invokes review.startRun through ADE actions without dropping unlimited budgets", async () => {
const fixture = createRuntime();
const startArgs = {
Expand Down
Loading
Loading