Expose Linear GraphQL through ADE CLI#451
Conversation
Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
📝 WalkthroughWalkthroughThis PR adds Linear GraphQL query execution via a new ChangesLinear GraphQL execution and session-linked publishing
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
|
@copilot review but do not make fixes |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Get PR details
gh pr view 451 --repo arul28/ADE --json title,body,additions,deletions,changedFiles,baseRefName,headRefName 2>&1
echo "---FILES CHANGED---"
gh pr diff 451 --repo arul28/ADE 2>&1 | head -3000Length of output: 53592 The PR description images show the "Open in Web" and "Open in Cursor" buttons linking to the Cursor agent session that generated this PR.
PR Review: Expose Linear GraphQL through ADE CLISummaryWell-structured PR with good test coverage across CLI planning, TUI routing, RPC execution, and lane/session card hooks. A few items worth addressing: 🔴 Issues1.
|
There was a problem hiding this comment.
PR Review
Scope: 21 file(s), +570 / −54
Verdict: Needs changes
This PR adds ade linear graphql (CLI/TUI/ADE actions), wires Linear lane/chat card publishing through laneService callbacks, and extends create-from-linear --start-chat to attach the issue after chat creation. The main risk is duplicate Linear lane attachments when a laned Work chat links an issue through the existing agent path.
🐛 Functionality
[High] Duplicate Linear lane cards on laned chat attach
File: apps/desktop/src/main/services/lanes/laneService.ts:L2768-L2773 and apps/desktop/src/main/services/lanes/laneService.ts:L2856-L2862
Issue: linkLinearIssues and attachLinearIssueToSession (lane mirror path) both call notifyLinearIssueLinked, which posts a lane attachment/comment via publishLinearLaneCard. The agent chat path runs both in sequence for the same issue.
Repro: Open a Work chat with a laneId. Attach a Linear issue that is not the lane's primary issue (e.g. via agent context / recordLinearIssueContextForLane). attachLinearIssueToSession mirrors into lane_linear_issue_links and notifies once; linkLinearIssues upserts the same worked link and notifies again. Linear shows two ADE lane cards/comments for one attach.
Fix: Notify from only one layer (e.g. drop notifyLinearIssueLinked from linkLinearIssues when source === "chat_attach", or skip the linkLinearIssues call in agentChatService when attachLinearIssueToSession already mirrored). Alternatively dedupe lane-card publishes the same way chat cards use linearChatCardPublishKeys.
🎨 UI/UX
[Low] TUI drops invalid GraphQL variables silently
File: apps/ade-cli/src/tuiClient/linearCommands.ts:L164-L175
Issue: /linear graphql with malformed --variables-json parses to undefined and runs the query without variables instead of surfacing a usage error, so the user sees a confusing Linear API failure.
Fix: On JSON.parse failure or non-object payload, return a usage(...) error (match CLI readLinearGraphQLArgs, which throws CliUsageError for bad --variables-json).
Notes
- GraphQL passthrough is intentionally broad (full Linear API with project OAuth). It is gated by existing ADE action allowlisting and runtime auth like other
linear_issue_trackerwrites; not flagged as a new auth bypass. - In-process dedupe for chat session cards (
linearChatCardPublishKeys) should prevent duplicate chat attachments whenonLinearIssueSessionLinkedandonLinearIssueChatLinkedboth fire; lane cards have no equivalent dedupe today. - Bundling
ade-linearin desktop artifacts and agent guidance looks consistent with the new surface.
Sent by Cursor Automation: BUGBOT in Versic
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/ade-cli/src/tuiClient/linearCommands.ts (1)
428-431:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winKeep the fallback
/linearusage list in sync with the newgraphqlcommand.The bottom fallback usage text still omits
graphql, so users can get contradictory help text depending on path.💡 Suggested fix
return usage( "Linear", - "Usage: /linear <attach|detach|issues|comment|set-state|assign|label|issue|create-from|workflows|run|route|sync|ingress> ...", + "Usage: /linear <attach|detach|issues|comment|set-state|assign|label|issue|graphql|create-from|workflows|run|route|sync|ingress> ...", );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/ade-cli/src/tuiClient/linearCommands.ts` around lines 428 - 431, The fallback help string returned by usage in linearCommands.ts is missing the new graphql command; update the string passed to usage (the "Usage: /linear <...>" literal) to include "graphql" in the angle-bracket command list so the fallback output from the usage(...) call matches the added graphql command (locate the usage(...) call in linearCommands.ts that returns the "Usage: /linear <attach|...|ingress>" string and insert "|graphql" at the appropriate position).
🧹 Nitpick comments (2)
apps/desktop/src/main/services/adeActions/registry.test.ts (1)
388-400: ⚡ Quick winAdd coverage for the
runGraphQLalias as well.Line 388 and Lines 394-400 verify
graphql, butlinear_issue_trackernow exposes both aliases. Add a matching assertion/invocation forrunGraphQLto lock parity and prevent alias drift.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/main/services/adeActions/registry.test.ts` around lines 388 - 400, Tests currently cover the "graphql" alias but not the "runGraphQL" alias; add parity by asserting listAllowedAdeActionNames("linear_issue_tracker", service) contains "runGraphQL", then invoke service.runGraphQL with the same query/variables used for service.graphql and assert it resolves to the same payload, and finally assert tracker.runGraphQL was called with the same argument object (or add a second call expectation / adjust call count accordingly) so both the action list and runtime alias behavior are covered.apps/ade-cli/src/bootstrap.ts (1)
497-520: ⚡ Quick winExtract the shared chat-card publish path.
These two blocks duplicate the same dedupe/publish/error-handling flow. Pulling them into one local helper will keep lane-service and agent-chat publishing behavior from drifting the next time this logic changes.
♻️ Suggested consolidation
+ const publishLinearChatLink = ({ + laneId, + sessionId, + sessionTitle, + issue, + linkedAt, + }: { + laneId: string | null; + sessionId: string; + sessionTitle: string | null; + issue: { id: string; identifier: string }; + linkedAt: string; + }) => { + const tracker = linearIssueTrackerRef; + if (!tracker) return; + const key = `${issue.id}:${sessionId}`; + if (linearChatCardPublishKeys.has(key)) return; + linearChatCardPublishKeys.add(key); + void publishLinearChatSessionCard({ + issueTracker: tracker, + issue, + laneId, + sessionId, + sessionTitle, + linkedAt, + }).catch((error) => { + linearChatCardPublishKeys.delete(key); + logger.warn("linear.chat_session_card_publish_failed", { + laneId, + sessionId, + issueId: issue.id, + issueIdentifier: issue.identifier, + error: error instanceof Error ? error.message : String(error), + }); + }); + }; + - onLinearIssueSessionLinked: ({ laneId, sessionId, sessionTitle, issue, linkedAt }) => { - const tracker = linearIssueTrackerRef; - if (!tracker) return; - const key = `${issue.id}:${sessionId}`; - if (linearChatCardPublishKeys.has(key)) return; - linearChatCardPublishKeys.add(key); - void publishLinearChatSessionCard({ - issueTracker: tracker, - issue, - laneId, - sessionId, - sessionTitle, - linkedAt, - }).catch((error) => { - linearChatCardPublishKeys.delete(key); - logger.warn("linear.chat_session_card_publish_failed", { - laneId, - sessionId, - issueId: issue.id, - issueIdentifier: issue.identifier, - error: error instanceof Error ? error.message : String(error), - }); - }); - }, + onLinearIssueSessionLinked: publishLinearChatLink, ... - onLinearIssueChatLinked: ({ laneId, sessionId, sessionTitle, issue, linkedAt }) => { - const tracker = linearIssueTrackerRef; - if (!tracker) return; - const key = `${issue.id}:${sessionId}`; - if (linearChatCardPublishKeys.has(key)) return; - linearChatCardPublishKeys.add(key); - void publishLinearChatSessionCard({ - issueTracker: tracker, - issue, - laneId, - sessionId, - sessionTitle, - linkedAt, - }).catch((error) => { - linearChatCardPublishKeys.delete(key); - logger.warn("linear.chat_session_card_publish_failed", { - laneId, - sessionId, - issueId: issue.id, - issueIdentifier: issue.identifier, - error: error instanceof Error ? error.message : String(error), - }); - }); - }, + onLinearIssueChatLinked: publishLinearChatLink,Also applies to: 965-988
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/ade-cli/src/bootstrap.ts` around lines 497 - 520, The onLinearIssueSessionLinked block duplicates the dedupe/publish/error-handling flow for linear chat-card publishing; extract a local helper (e.g., publishLinearChatCardOnce) that accepts parameters { tracker, issue, laneId, sessionId, sessionTitle, linkedAt } and encapsulates the linearChatCardPublishKeys check, key construction `${issue.id}:${sessionId}`, adding/removing keys, calling publishLinearChatSessionCard, and the logger.warn(error) handling; replace the body of onLinearIssueSessionLinked and the similar block (lines ~965-988) to call this helper, referencing linearIssueTrackerRef, linearChatCardPublishKeys, publishLinearChatSessionCard, and logger.warn for locating the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/ade-cli/src/cli.ts`:
- Around line 2075-2098: readLinearGraphQLArgs currently calls
collectGenericObjectArgs which can overwrite reserved GraphQL fields (query,
variables, maxRetries) validated earlier; update the function so either (A)
prevent overrides by calling collectGenericObjectArgs with a mechanism to block
reserved keys (query, variables, operationName, maxRetries) or (B) re-validate
and normalize the returned object: ensure result.query is present and equal to
the previously determined query (or throw), ensure result.variables (from
readJsonPayloadOption or overridden) is either undefined or a record (throw if
not), and ensure result.maxRetries is undefined or a number (coerce/throw if
invalid); implement this change in readLinearGraphQLArgs around the
collectGenericObjectArgs call and preserve the original semantics of
maybePut/readNumberOption/readJsonPayloadOption.
In `@apps/ade-cli/src/tuiClient/linearCommands.ts`:
- Around line 164-176: The parseGraphQLVariables function currently swallows
malformed or non-object JSON and returns undefined, causing invalid
--variables-json to be silently ignored; update parseGraphQLVariables to
validate the optionString value and, on JSON parse errors or when the parsed
value is not a plain object, throw or return a user-facing error (e.g., throw a
descriptive Error or return a Result/Err) so the CLI surfaces a clear validation
message; apply the same change to the other variables-parsing helper used around
the second occurrence (the similar function at lines ~290-305) so both malformed
JSON and non-object JSON input produce an explicit error instead of silently
dropping variables.
In `@apps/desktop/src/main/main.ts`:
- Around line 1765-1808: The handler publishLinearChatLink currently uses
linearChatCardPublishKeys to short-circuit the entire function (skipping both
publishLinearChatSessionCard and linearLiveStatusServiceRef.onAgentLaunched)
which prevents retries of the live-status path; change the logic so deduping
only affects publishLinearChatSessionCard (e.g. only skip calling
publishLinearChatSessionCard when linearChatCardPublishKeys.has(key) but always
proceed to call linearLiveStatusServiceRef?.onAgentLaunched), or maintain a
separate dedupe set for the live-status path; update publishLinearChatLink to
ensure linearLiveStatusServiceRef.onAgentLaunched is invoked on every event (and
still catches/logs errors) even when the publish key exists.
---
Outside diff comments:
In `@apps/ade-cli/src/tuiClient/linearCommands.ts`:
- Around line 428-431: The fallback help string returned by usage in
linearCommands.ts is missing the new graphql command; update the string passed
to usage (the "Usage: /linear <...>" literal) to include "graphql" in the
angle-bracket command list so the fallback output from the usage(...) call
matches the added graphql command (locate the usage(...) call in
linearCommands.ts that returns the "Usage: /linear <attach|...|ingress>" string
and insert "|graphql" at the appropriate position).
---
Nitpick comments:
In `@apps/ade-cli/src/bootstrap.ts`:
- Around line 497-520: The onLinearIssueSessionLinked block duplicates the
dedupe/publish/error-handling flow for linear chat-card publishing; extract a
local helper (e.g., publishLinearChatCardOnce) that accepts parameters {
tracker, issue, laneId, sessionId, sessionTitle, linkedAt } and encapsulates the
linearChatCardPublishKeys check, key construction `${issue.id}:${sessionId}`,
adding/removing keys, calling publishLinearChatSessionCard, and the
logger.warn(error) handling; replace the body of onLinearIssueSessionLinked and
the similar block (lines ~965-988) to call this helper, referencing
linearIssueTrackerRef, linearChatCardPublishKeys, publishLinearChatSessionCard,
and logger.warn for locating the code.
In `@apps/desktop/src/main/services/adeActions/registry.test.ts`:
- Around line 388-400: Tests currently cover the "graphql" alias but not the
"runGraphQL" alias; add parity by asserting
listAllowedAdeActionNames("linear_issue_tracker", service) contains
"runGraphQL", then invoke service.runGraphQL with the same query/variables used
for service.graphql and assert it resolves to the same payload, and finally
assert tracker.runGraphQL was called with the same argument object (or add a
second call expectation / adjust call count accordingly) so both the action list
and runtime alias behavior are covered.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c5b31b15-5c44-4aad-bbcf-2ea9085c2692
📒 Files selected for processing (21)
apps/ade-cli/src/adeRpcServer.test.tsapps/ade-cli/src/bootstrap.tsapps/ade-cli/src/cli.test.tsapps/ade-cli/src/cli.tsapps/ade-cli/src/tuiClient/__tests__/commands.test.tsapps/ade-cli/src/tuiClient/linearCommands.tsapps/desktop/resources/agent-skills/ade-deeplinks/SKILL.mdapps/desktop/resources/agent-skills/ade-linear/SKILL.mdapps/desktop/scripts/validate-mac-artifacts.mjsapps/desktop/scripts/validate-win-artifacts.mjsapps/desktop/src/main/main.tsapps/desktop/src/main/services/adeActions/registry.test.tsapps/desktop/src/main/services/adeActions/registry.tsapps/desktop/src/main/services/chat/agentChatService.test.tsapps/desktop/src/main/services/chat/agentChatService.tsapps/desktop/src/main/services/cto/issueTracker.tsapps/desktop/src/main/services/cto/linearClient.tsapps/desktop/src/main/services/cto/linearIssueTracker.tsapps/desktop/src/main/services/lanes/laneService.test.tsapps/desktop/src/main/services/lanes/laneService.tsapps/desktop/src/shared/adeCliGuidance.ts
|
Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews. |


Summary
ade linear graphqland/linear graphqlthrough the runtime-owned Linear issue tracker so agents can use the saved project Linear OAuth/API key without receiving token material.lanes create-from-linear --start-chat.ade-linear/ade-deeplinksskills so agents know when ADE posts cards automatically and when direct Linear actions should include ADE deeplinks in comments.Testing
npm --prefix apps/ade-cli run test -- src/cli.test.ts src/tuiClient/__tests__/commands.test.ts src/adeRpcServer.test.tsnpm --prefix apps/desktop run test -- src/main/services/lanes/laneService.test.ts src/main/services/chat/agentChatService.test.ts src/main/services/adeActions/registry.test.ts src/main/services/cto/linearLaneCardService.test.tsnpm --prefix apps/desktop run test -- src/main/services/adeActions/registry.test.ts src/main/services/cto/linearAuth.test.tsnpm --prefix apps/ade-cli run typechecknpm --prefix apps/desktop run typechecknpm --prefix apps/ade-cli run buildnpm --prefix apps/desktop run lint(passes with existing warnings)npm --prefix apps/desktop run build(passes with existing Vite chunk-size warnings)Summary by CodeRabbit
Release Notes
New Features
ade linear graphqlcommand for executing GraphQL queries directly against LinearDocumentation
Greptile Summary
This PR adds
ade linear graphql//linear graphqlcommands that proxy raw GraphQL through the runtime-owned Linear OAuth connection, wires deterministic Linear lane/chat card publishing tolaneService.onLinearIssueLinkedandonLinearIssueSessionLinked(headless and desktop), inserts an explicitattachLinearIssueToSessionstep into thecreate-from-linear --start-chatplan so the runtime posts chat cards, and promotes theade-linear/ade-deeplinksskills to the bundled set.readLinearGraphQLArgs/parseGraphQLVariableswith full validation (inline, file, positional query; variables must be a JSON object; re-validated after--arg-jsonoverrides); both CLI and TUI paths tested.notifyLinearIssueLinkedwired intolinkLinearIssues(guarded bysource !== \"chat_attach\") andattachLinearIssueToSession(for newly mirrored lane links); independent dedup sets prevent duplicate cards and duplicateonAgentLaunchedcalls.ade-linearadded to the bundled agent skills manifest on Mac, Windows, and the shared guidance list; both skill docs updated with GraphQL examples and the ADE-deeplinks matrix."Confidence Score: 4/5
Safe to merge after fixing the
linkedAttimestamp passed to Linear lane card attachments.The new
notifyLinearIssueLinkedcall sites in bothlinkLinearIssuesandattachLinearIssueToSessionpasslane.createdAtas thelinkedAtmetadata written to Linear, instead of the actual link-creation timestamp. For new lanes this difference is negligible, but for issues linked to an existing lane the attachment in Linear will show the wrong date. The GraphQL passthrough, dedup guards, and skill bundles are well-structured and thoroughly tested.apps/desktop/src/main/services/lanes/laneService.ts — the
notifyLinearIssueLinkedcall sites should pass the link's owncreatedAtrather than relying onlane.createdAt.Important Files Changed
onLinearIssueSessionLinkedcallback,resolveSessionTitle,notifyLinearIssueSessionLinked, andlaneSummaryForLinearNotification; wiresnotifyLinearIssueLinkedintolinkLinearIssuesandattachLinearIssueToSession. The new call sites uselane.createdAtaslinkedAtinstead of the link's owncreatedAt.publishLinearChatLinkclosure with independent dedup sets for cards and foronAgentLaunched; subscribes it to bothlaneService.onLinearIssueSessionLinkedand the existingagentChatService.onLinearIssueChatLinked.onLinearIssueLinked(lane cards) andonLinearIssueSessionLinked(session cards) subscribers tolaneService; initialiseslinearIssueTrackerRef/githubServiceRefrefs aftercreateHeadlessLinearServicesso callbacks safely guard via the refs.ade linear graphqlsub-command withreadLinearGraphQLArgs(inline, file, and positional query; variables validation; post-override re-validation); inserts anattachLinearIssueToSessionstep intocreate-from-linear --start-chatplan./linear graphqlTUI routing withparseGraphQLVariablesreturning a typed result union; correctly surfaces parse errors back to the user and updates both usage strings to includegraphql.graphqlandrunGraphQLaliases in thelinear_issue_trackerdomain service and allowlist, both validated throughreadLinearGraphQLActionArgsbefore forwarding totracker.runGraphQL.runGraphQLwrapper and threadsoperationNamethrough the existingrequesthelper's JSON body.runGraphQLto theIssueTrackerinterface, cleanly extending the contract for all implementors.ade-deeplinksskill reference tobuildLinearSessionDirectiveso agents are guided to include ADE deeplinks in Linear comments.Sequence Diagram
sequenceDiagram participant CLI as ade CLI / TUI participant RPC as ADE RPC Server participant Reg as Action Registry participant Tracker as LinearIssueTracker participant Linear as Linear API Note over CLI,Linear: ade linear graphql CLI->>RPC: "run_ade_action(linear_issue_tracker, graphql, {query, variables})" RPC->>Reg: dispatch(graphql, args) Reg->>Reg: readLinearGraphQLActionArgs(args) Reg->>Tracker: "runGraphQL({query, variables, operationName})" Tracker->>Linear: POST /graphql (Bearer token) Linear-->>Tracker: response data Tracker-->>CLI: result Note over CLI,Linear: create-from-linear --start-chat CLI->>RPC: Step 1 - create_lane (with Linear issue) RPC-->>CLI: "{laneId}" CLI->>RPC: Step 2 - chat createSession RPC-->>CLI: "{sessionId}" CLI->>RPC: "Step 3 - attachLinearIssueToSession(sessionId, issue, source=chat_attach)" RPC->>Tracker: onLinearIssueSessionLinked → publishLinearChatSessionCard Tracker->>Linear: create attachment (chat card) CLI->>RPC: Step 4 - send kickoff messageComments Outside Diff (2)
apps/ade-cli/src/tuiClient/linearCommands.ts, line 395-399 (link)--variables-jsonparseGraphQLVariablesswallows both JSON parse failures and non-object values, returningundefinedwithout any diagnostic. A user who mistypes'{first:10}'(unquoted key) or passes an array'[1,2]'will silently get a request with no variables. The CLI path (readLinearGraphQLArgs) correctly throws aCliUsageErrorin both cases. The TUI path should give the same feedback so the author can fix the input instead of debugging a mysteriously missing parameter.Prompt To Fix With AI
apps/ade-cli/src/tuiClient/linearCommands.ts, line 428-431 (link)graphqlwas added, so a user who mistypes the subcommand gets a help message that omits the new command.Prompt To Fix With AI
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix All With AI
Reviews (4): Last reviewed commit: "ship: dedupe Linear live-status updates" | Re-trigger Greptile