Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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