From bad304c2d343572a6479b6a915e7c20692c26cd7 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 7 May 2026 15:23:45 +0700 Subject: [PATCH] Detect GitHub issue/PR URL in composer search Pasting a GitHub issue or pull request URL into the composer's issue/PR picker now searches by the number from the URL instead of treating the full URL as a keyword that returns no matches. --- .../src/services/github-service.test.ts | 38 +++++++++++++++++++ .../server/src/services/github-service.ts | 14 ++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/packages/server/src/services/github-service.test.ts b/packages/server/src/services/github-service.test.ts index e9567d908..214d4eb00 100644 --- a/packages/server/src/services/github-service.test.ts +++ b/packages/server/src/services/github-service.test.ts @@ -1698,6 +1698,44 @@ describe("GitHubService", () => { ]); }); + it("treats a GitHub issue or PR URL as a search for that number", async () => { + const runner = createRunner([issueJson("Issue title"), searchPullRequestJson("PR title")]); + const service = createGitHubService({ + runner: runner.runner, + resolveGhPath: async () => "/usr/bin/gh", + now: () => 100, + }); + + await service.searchIssuesAndPrs({ + cwd: "/repo", + query: "https://github.com/getpaseo/paseo/pull/793", + limit: 5, + }); + + expect(runner.calls.map((call) => call.args)).toEqual([ + [ + "issue", + "list", + "--search", + "793", + "--json", + "number,title,url,state,body,labels,updatedAt", + "--limit", + "5", + ], + [ + "pr", + "list", + "--search", + "793", + "--json", + "number,title,url,state,body,labels,baseRefName,headRefName,updatedAt", + "--limit", + "5", + ], + ]); + }); + it("searches only GitHub PRs when the search kinds request excludes issues", async () => { const runner = createRunner([searchPullRequestJson("PR title")]); const service = createGitHubService({ diff --git a/packages/server/src/services/github-service.ts b/packages/server/src/services/github-service.ts index e39063a70..99ebb84c9 100644 --- a/packages/server/src/services/github-service.ts +++ b/packages/server/src/services/github-service.ts @@ -933,11 +933,12 @@ export function createGitHubService(options: CreateGitHubServiceOptions = {}): G const readOptions: GitHubReadOptions = input.force ? { force: true, reason: input.reason } : { force: false, reason: input.reason }; + const query = normalizeGitHubSearchQuery(input.query); const [issuesResult, prsResult] = await Promise.allSettled([ shouldFetchIssues ? this.listIssues({ cwd: input.cwd, - query: input.query, + query, limit: input.limit, ...readOptions, }) @@ -945,7 +946,7 @@ export function createGitHubService(options: CreateGitHubServiceOptions = {}): G shouldFetchPullRequests ? this.listPullRequests({ cwd: input.cwd, - query: input.query, + query, limit: input.limit, ...readOptions, }) @@ -1171,6 +1172,15 @@ async function runGhCommand( }); } +const GITHUB_ISSUE_OR_PR_URL_PATTERN = + /^https?:\/\/github\.com\/[^/\s]+\/[^/\s]+\/(?:pull|issues)\/(\d+)(?:[/?#].*)?$/i; + +function normalizeGitHubSearchQuery(query: string): string { + const trimmed = query.trim(); + const match = trimmed.match(GITHUB_ISSUE_OR_PR_URL_PATTERN); + return match ? match[1] : query; +} + function buildCacheKey(params: { cwd: string; method: string; args: unknown }): string { return `${params.cwd}:${params.method}:${stableStringify(params.args)}`; }