@gitkraken/core-gitlens 0.5.109 → 0.5.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -1
- package/dist/git/cache.d.ts +111 -6
- package/dist/git/cache.d.ts.map +1 -1
- package/dist/git/cache.js +312 -43
- package/dist/git/cache.js.map +1 -1
- package/dist/git/context.d.ts +11 -0
- package/dist/git/context.d.ts.map +1 -1
- package/dist/git/context.js.map +1 -1
- package/dist/git/models/fileStatus.d.ts +1 -0
- package/dist/git/models/fileStatus.d.ts.map +1 -1
- package/dist/git/models/fileStatus.js +1 -0
- package/dist/git/models/fileStatus.js.map +1 -1
- package/dist/git/models/issue.d.ts +4 -0
- package/dist/git/models/issue.d.ts.map +1 -1
- package/dist/git/models/mergeConflicts.d.ts +2 -0
- package/dist/git/models/mergeConflicts.d.ts.map +1 -1
- package/dist/git/models/pullRequest.d.ts +119 -2
- package/dist/git/models/pullRequest.d.ts.map +1 -1
- package/dist/git/models/pullRequest.js +27 -1
- package/dist/git/models/pullRequest.js.map +1 -1
- package/dist/git/models/repository.d.ts +32 -5
- package/dist/git/models/repository.d.ts.map +1 -1
- package/dist/git/models/repository.js +58 -12
- package/dist/git/models/repository.js.map +1 -1
- package/dist/git/models/statusFile.d.ts.map +1 -1
- package/dist/git/models/statusFile.js +3 -0
- package/dist/git/models/statusFile.js.map +1 -1
- package/dist/git/providers/branches.d.ts +2 -0
- package/dist/git/providers/branches.d.ts.map +1 -1
- package/dist/git/providers/commits.d.ts +8 -0
- package/dist/git/providers/commits.d.ts.map +1 -1
- package/dist/git/providers/config.d.ts +19 -1
- package/dist/git/providers/config.d.ts.map +1 -1
- package/dist/git/providers/operations.d.ts +1 -0
- package/dist/git/providers/operations.d.ts.map +1 -1
- package/dist/git/providers/pausedOperations.d.ts +2 -0
- package/dist/git/providers/pausedOperations.d.ts.map +1 -1
- package/dist/git/providers/status.d.ts +1 -0
- package/dist/git/providers/status.d.ts.map +1 -1
- package/dist/git/utils/config.utils.d.ts +3 -0
- package/dist/git/utils/config.utils.d.ts.map +1 -0
- package/dist/git/utils/config.utils.js +8 -0
- package/dist/git/utils/config.utils.js.map +1 -0
- package/dist/git/utils/issue.utils.d.ts.map +1 -1
- package/dist/git/utils/issue.utils.js +2 -0
- package/dist/git/utils/issue.utils.js.map +1 -1
- package/dist/git/utils/pausedOperationStatus.utils.d.ts +14 -0
- package/dist/git/utils/pausedOperationStatus.utils.d.ts.map +1 -1
- package/dist/git/utils/pausedOperationStatus.utils.js +23 -0
- package/dist/git/utils/pausedOperationStatus.utils.js.map +1 -1
- package/dist/git/utils/pullRequest.utils.d.ts +24 -1
- package/dist/git/utils/pullRequest.utils.d.ts.map +1 -1
- package/dist/git/utils/pullRequest.utils.js +71 -0
- package/dist/git/utils/pullRequest.utils.js.map +1 -1
- package/dist/git/watching/changeEvent.d.ts +6 -1
- package/dist/git/watching/changeEvent.d.ts.map +1 -1
- package/dist/git/watching/classifyChange.d.ts.map +1 -1
- package/dist/git/watching/classifyChange.js +14 -1
- package/dist/git/watching/classifyChange.js.map +1 -1
- package/dist/git/watching/watchService.d.ts.map +1 -1
- package/dist/git/watching/watchService.js +18 -3
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +8 -1
- package/dist/git/watching/watchSession.d.ts.map +1 -1
- package/dist/git/watching/watchSession.js +38 -6
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git/watching/watcherPatterns.d.ts +2 -2
- package/dist/git/watching/watcherPatterns.d.ts.map +1 -1
- package/dist/git/watching/watcherPatterns.js +1 -1
- package/dist/git/watching/watcherPatterns.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +1 -1
- package/dist/git-cli/exec/git.d.ts.map +1 -1
- package/dist/git-cli/exec/git.js +13 -3
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts +19 -0
- package/dist/git-cli/providers/branches.d.ts.map +1 -1
- package/dist/git-cli/providers/branches.js +282 -78
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/commits.d.ts +4 -0
- package/dist/git-cli/providers/commits.d.ts.map +1 -1
- package/dist/git-cli/providers/commits.js +84 -11
- package/dist/git-cli/providers/commits.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts +42 -0
- package/dist/git-cli/providers/config.d.ts.map +1 -1
- package/dist/git-cli/providers/config.js +186 -5
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/contributors.d.ts.map +1 -1
- package/dist/git-cli/providers/contributors.js +1 -1
- package/dist/git-cli/providers/contributors.js.map +1 -1
- package/dist/git-cli/providers/graph.d.ts +2 -2
- package/dist/git-cli/providers/graph.d.ts.map +1 -1
- package/dist/git-cli/providers/graph.js +27 -9
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts.map +1 -1
- package/dist/git-cli/providers/operations.js +40 -12
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.d.ts +5 -0
- package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
- package/dist/git-cli/providers/pausedOperations.js +87 -16
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.d.ts +5 -3
- package/dist/git-cli/providers/refs.d.ts.map +1 -1
- package/dist/git-cli/providers/refs.js +33 -4
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/git-cli/providers/status.d.ts +1 -0
- package/dist/git-cli/providers/status.d.ts.map +1 -1
- package/dist/git-cli/providers/status.js +18 -3
- package/dist/git-cli/providers/status.js.map +1 -1
- package/dist/git-cli/providers/worktrees.d.ts.map +1 -1
- package/dist/git-cli/providers/worktrees.js +4 -0
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/git-cli/service.d.ts.map +1 -1
- package/dist/git-cli/service.js.map +1 -1
- package/dist/plus/agents/providers/claudeCodeProvider.d.ts +39 -1
- package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +1 -1
- package/dist/plus/agents/providers/claudeCodeProvider.js +978 -71
- package/dist/plus/agents/providers/claudeCodeProvider.js.map +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +24 -2
- package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.js +84 -9
- package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
- package/dist/plus/agents/types.d.ts +37 -2
- package/dist/plus/agents/types.d.ts.map +1 -1
- package/dist/plus/agents/types.js +2 -0
- package/dist/plus/agents/types.js.map +1 -1
- package/dist/plus/ai/errors.d.ts +10 -0
- package/dist/plus/ai/errors.d.ts.map +1 -1
- package/dist/plus/ai/errors.js +29 -0
- package/dist/plus/ai/errors.js.map +1 -1
- package/dist/plus/ai/models/model.d.ts +4 -0
- package/dist/plus/ai/models/model.d.ts.map +1 -1
- package/dist/plus/ai/models/model.js +51 -6
- package/dist/plus/ai/models/model.js.map +1 -1
- package/dist/plus/ai/models/promptTemplates.d.ts +1 -1
- package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
- package/dist/plus/ai/models/provider.d.ts +60 -2
- package/dist/plus/ai/models/provider.d.ts.map +1 -1
- package/dist/plus/ai/prompts.d.ts +12 -0
- package/dist/plus/ai/prompts.d.ts.map +1 -1
- package/dist/plus/ai/prompts.js +177 -61
- package/dist/plus/ai/prompts.js.map +1 -1
- package/dist/plus/ai/providers/anthropicProvider.d.ts +10 -2
- package/dist/plus/ai/providers/anthropicProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/anthropicProvider.js +139 -46
- package/dist/plus/ai/providers/anthropicProvider.js.map +1 -1
- package/dist/plus/ai/providers/deepSeekProvider.d.ts +2 -0
- package/dist/plus/ai/providers/deepSeekProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/deepSeekProvider.js +10 -21
- package/dist/plus/ai/providers/deepSeekProvider.js.map +1 -1
- package/dist/plus/ai/providers/geminiProvider.d.ts +1 -0
- package/dist/plus/ai/providers/geminiProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/geminiProvider.js +19 -0
- package/dist/plus/ai/providers/geminiProvider.js.map +1 -1
- package/dist/plus/ai/providers/gitkrakenProvider.d.ts +5 -1
- package/dist/plus/ai/providers/gitkrakenProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/gitkrakenProvider.js +118 -3
- package/dist/plus/ai/providers/gitkrakenProvider.js.map +1 -1
- package/dist/plus/ai/providers/huggingFaceProvider.d.ts +1 -0
- package/dist/plus/ai/providers/huggingFaceProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/huggingFaceProvider.js +5 -0
- package/dist/plus/ai/providers/huggingFaceProvider.js.map +1 -1
- package/dist/plus/ai/providers/mistralProvider.d.ts +3 -1
- package/dist/plus/ai/providers/mistralProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/mistralProvider.js +99 -19
- package/dist/plus/ai/providers/mistralProvider.js.map +1 -1
- package/dist/plus/ai/providers/ollamaProvider.d.ts +4 -3
- package/dist/plus/ai/providers/ollamaProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/ollamaProvider.js +38 -3
- package/dist/plus/ai/providers/ollamaProvider.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProvider.d.ts +1 -0
- package/dist/plus/ai/providers/openAICompatibleProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProvider.js +4 -0
- package/dist/plus/ai/providers/openAICompatibleProvider.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts +154 -5
- package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js +259 -10
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js.map +1 -1
- package/dist/plus/ai/providers/openRouterProvider.d.ts +4 -0
- package/dist/plus/ai/providers/openRouterProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/openRouterProvider.js +9 -0
- package/dist/plus/ai/providers/openRouterProvider.js.map +1 -1
- package/dist/plus/ai/providers/openaiProvider.d.ts +1 -0
- package/dist/plus/ai/providers/openaiProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/openaiProvider.js +1 -0
- package/dist/plus/ai/providers/openaiProvider.js.map +1 -1
- package/dist/plus/ai/providers/responseFormatCache.d.ts +8 -0
- package/dist/plus/ai/providers/responseFormatCache.d.ts.map +1 -0
- package/dist/plus/ai/providers/responseFormatCache.js +25 -0
- package/dist/plus/ai/providers/responseFormatCache.js.map +1 -0
- package/dist/plus/ai/providers/xaiProvider.d.ts +1 -0
- package/dist/plus/ai/providers/xaiProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/xaiProvider.js +10 -1
- package/dist/plus/ai/providers/xaiProvider.js.map +1 -1
- package/dist/plus/ai/utils/ai.utils.d.ts +3 -0
- package/dist/plus/ai/utils/ai.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/ai.utils.js +13 -0
- package/dist/plus/ai/utils/ai.utils.js.map +1 -1
- package/dist/plus/ai/utils/results.utils.d.ts +16 -2
- package/dist/plus/ai/utils/results.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/results.utils.js +204 -28
- package/dist/plus/ai/utils/results.utils.js.map +1 -1
- package/dist/plus/git-github/api/github.d.ts +92 -4
- package/dist/plus/git-github/api/github.d.ts.map +1 -1
- package/dist/plus/git-github/api/github.js +324 -20
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts +27 -1
- package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts.map +1 -1
- package/dist/plus/git-github/api/pullRequestSearchQuery.js +73 -6
- package/dist/plus/git-github/api/pullRequestSearchQuery.js.map +1 -1
- package/dist/plus/git-github/models.d.ts +33 -5
- package/dist/plus/git-github/models.d.ts.map +1 -1
- package/dist/plus/git-github/models.js +43 -4
- package/dist/plus/git-github/models.js.map +1 -1
- package/dist/plus/git-github/providers/github/status.d.ts +1 -0
- package/dist/plus/git-github/providers/github/status.d.ts.map +1 -1
- package/dist/plus/git-github/providers/github/status.js.map +1 -1
- package/dist/plus/integrations/context.d.ts +4 -0
- package/dist/plus/integrations/context.d.ts.map +1 -1
- package/dist/plus/integrations/index.d.ts.map +1 -1
- package/dist/plus/integrations/index.js +2 -0
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +16 -1
- package/dist/plus/integrations/integrationService.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.js +9 -1
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +45 -5
- package/dist/plus/integrations/manager.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +18 -3
- package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +30 -2
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +4 -2
- package/dist/plus/integrations/models/integration.d.ts.map +1 -1
- package/dist/plus/integrations/models/integration.js +2 -2
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/providerFilters.d.ts +1 -1
- package/dist/plus/integrations/providerFilters.d.ts.map +1 -1
- package/dist/plus/integrations/providers/accounts.d.ts +6 -0
- package/dist/plus/integrations/providers/accounts.d.ts.map +1 -0
- package/dist/plus/integrations/providers/accounts.js +34 -0
- package/dist/plus/integrations/providers/accounts.js.map +1 -0
- package/dist/plus/integrations/providers/azure/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/azure/models.js +3 -0
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/bitbucket/models.js +1 -0
- package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +23 -2
- package/dist/plus/integrations/providers/github.d.ts.map +1 -1
- package/dist/plus/integrations/providers/github.js +169 -138
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.d.ts +9 -0
- package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -1
- package/dist/plus/integrations/providers/gitlab.js +23 -4
- package/dist/plus/integrations/providers/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +14 -32
- package/dist/plus/integrations/providers/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/models.js +66 -109
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/pullRequestReviews.d.ts +50 -0
- package/dist/plus/integrations/providers/pullRequestReviews.d.ts.map +1 -0
- package/dist/plus/integrations/providers/pullRequestReviews.js +99 -0
- package/dist/plus/integrations/providers/pullRequestReviews.js.map +1 -0
- package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
- package/dist/plus/integrations/reads/broaden.js +6 -1
- package/dist/plus/integrations/reads/broaden.js.map +1 -1
- package/dist/plus/integrations/reads/counts.d.ts +55 -0
- package/dist/plus/integrations/reads/counts.d.ts.map +1 -1
- package/dist/plus/integrations/reads/counts.js +146 -2
- package/dist/plus/integrations/reads/counts.js.map +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +7 -1
- package/dist/plus/integrations/reads/drains.d.ts.map +1 -1
- package/dist/plus/integrations/reads/drains.js +20 -6
- package/dist/plus/integrations/reads/drains.js.map +1 -1
- package/dist/plus/integrations/reads/filters.d.ts.map +1 -1
- package/dist/plus/integrations/reads/filters.js +22 -0
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.d.ts.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +3 -1
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.d.ts.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +8 -3
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/issues.d.ts.map +1 -1
- package/dist/plus/integrations/reads/issues.js +9 -4
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequests.d.ts.map +1 -1
- package/dist/plus/integrations/reads/pullRequests.js +9 -4
- package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
- package/dist/plus/integrations/reads/searchPullRequests.d.ts +4 -3
- package/dist/plus/integrations/reads/searchPullRequests.d.ts.map +1 -1
- package/dist/plus/integrations/reads/searchPullRequests.js +6 -4
- package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
- package/dist/plus/integrations/reads/sweeps.js +1 -1
- package/dist/plus/integrations/reads/sweeps.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +2 -2
- package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +11 -2
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +6 -1
- package/dist/plus/integrations/results.d.ts.map +1 -1
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/utils/env/browser/base64.d.ts +1 -1
- package/dist/utils/env/browser/base64.d.ts.map +1 -1
- package/dist/utils/env/node/base64.d.ts +1 -1
- package/dist/utils/env/node/base64.d.ts.map +1 -1
- package/dist/utils/env/node/base64.js +2 -0
- package/dist/utils/env/node/base64.js.map +1 -1
- package/dist/utils/keys/chord.d.ts +52 -0
- package/dist/utils/keys/chord.d.ts.map +1 -0
- package/dist/utils/keys/chord.js +135 -0
- package/dist/utils/keys/chord.js.map +1 -0
- package/dist/utils/keys/keybinding.d.ts +53 -0
- package/dist/utils/keys/keybinding.d.ts.map +1 -0
- package/dist/utils/keys/keybinding.js +58 -0
- package/dist/utils/keys/keybinding.js.map +1 -0
- package/dist/utils/path.d.ts +11 -0
- package/dist/utils/path.d.ts.map +1 -1
- package/dist/utils/path.js +13 -0
- package/dist/utils/path.js.map +1 -1
- package/docs/integrations.md +76 -10
- package/docs/kepler-read-api-parity.md +9 -0
- package/package.json +13 -5
- package/src/git/cache.ts +383 -47
- package/src/git/context.ts +11 -0
- package/src/git/models/fileStatus.ts +1 -0
- package/src/git/models/issue.ts +4 -0
- package/src/git/models/mergeConflicts.ts +3 -2
- package/src/git/models/pullRequest.ts +117 -1
- package/src/git/models/repository.ts +58 -17
- package/src/git/models/statusFile.ts +3 -0
- package/src/git/providers/branches.ts +6 -0
- package/src/git/providers/commits.ts +11 -0
- package/src/git/providers/config.ts +18 -0
- package/src/git/providers/operations.ts +1 -0
- package/src/git/providers/pausedOperations.ts +2 -0
- package/src/git/providers/status.ts +1 -0
- package/src/git/utils/config.utils.ts +8 -0
- package/src/git/utils/issue.utils.ts +2 -0
- package/src/git/utils/pausedOperationStatus.utils.ts +28 -0
- package/src/git/utils/pullRequest.utils.ts +86 -0
- package/src/git/watching/changeEvent.ts +6 -1
- package/src/git/watching/classifyChange.ts +15 -1
- package/src/git/watching/watchService.ts +20 -3
- package/src/git/watching/watchSession.ts +41 -6
- package/src/git/watching/watcherPatterns.ts +1 -1
- package/src/git-cli/exec/git.ts +13 -3
- package/src/git-cli/providers/branches.ts +384 -132
- package/src/git-cli/providers/commits.ts +106 -15
- package/src/git-cli/providers/config.ts +229 -8
- package/src/git-cli/providers/contributors.ts +5 -1
- package/src/git-cli/providers/graph.ts +26 -13
- package/src/git-cli/providers/operations.ts +45 -20
- package/src/git-cli/providers/pausedOperations.ts +109 -17
- package/src/git-cli/providers/refs.ts +33 -3
- package/src/git-cli/providers/status.ts +25 -4
- package/src/git-cli/providers/worktrees.ts +5 -0
- package/src/git-cli/service.ts +0 -1
- package/src/plus/agents/providers/claudeCodeProvider.ts +1130 -87
- package/src/plus/agents/providers/claudeCodeTranscript.ts +95 -9
- package/src/plus/agents/types.ts +46 -2
- package/src/plus/ai/errors.ts +30 -0
- package/src/plus/ai/models/model.ts +56 -6
- package/src/plus/ai/models/promptTemplates.ts +1 -1
- package/src/plus/ai/models/provider.ts +69 -2
- package/src/plus/ai/prompts.ts +186 -61
- package/src/plus/ai/providers/anthropicProvider.ts +155 -47
- package/src/plus/ai/providers/deepSeekProvider.ts +11 -21
- package/src/plus/ai/providers/geminiProvider.ts +19 -0
- package/src/plus/ai/providers/gitkrakenProvider.ts +126 -3
- package/src/plus/ai/providers/huggingFaceProvider.ts +6 -0
- package/src/plus/ai/providers/mistralProvider.ts +102 -19
- package/src/plus/ai/providers/ollamaProvider.ts +55 -4
- package/src/plus/ai/providers/openAICompatibleProvider.ts +5 -0
- package/src/plus/ai/providers/openAICompatibleProviderBase.ts +360 -17
- package/src/plus/ai/providers/openRouterProvider.ts +17 -0
- package/src/plus/ai/providers/openaiProvider.ts +1 -0
- package/src/plus/ai/providers/responseFormatCache.ts +39 -0
- package/src/plus/ai/providers/xaiProvider.ts +10 -1
- package/src/plus/ai/utils/ai.utils.ts +14 -0
- package/src/plus/ai/utils/results.utils.ts +255 -31
- package/src/plus/git-github/api/github.ts +467 -23
- package/src/plus/git-github/api/pullRequestSearchQuery.ts +81 -7
- package/src/plus/git-github/models.ts +77 -7
- package/src/plus/git-github/providers/github/status.ts +1 -0
- package/src/plus/integrations/context.ts +4 -0
- package/src/plus/integrations/index.ts +2 -0
- package/src/plus/integrations/integrationService.ts +25 -2
- package/src/plus/integrations/manager.ts +50 -5
- package/src/plus/integrations/models/gitHostIntegration.ts +54 -2
- package/src/plus/integrations/models/integration.ts +24 -13
- package/src/plus/integrations/providerFilters.ts +6 -1
- package/src/plus/integrations/providers/accounts.ts +38 -0
- package/src/plus/integrations/providers/azure/models.ts +3 -0
- package/src/plus/integrations/providers/bitbucket/models.ts +1 -0
- package/src/plus/integrations/providers/github.ts +218 -145
- package/src/plus/integrations/providers/gitlab.ts +30 -20
- package/src/plus/integrations/providers/models.ts +99 -127
- package/src/plus/integrations/providers/pullRequestReviews.ts +122 -0
- package/src/plus/integrations/reads/broaden.ts +6 -1
- package/src/plus/integrations/reads/counts.ts +273 -2
- package/src/plus/integrations/reads/drains.ts +19 -6
- package/src/plus/integrations/reads/filters.ts +22 -0
- package/src/plus/integrations/reads/hierarchy.ts +3 -1
- package/src/plus/integrations/reads/issueTracker.ts +9 -3
- package/src/plus/integrations/reads/issues.ts +9 -4
- package/src/plus/integrations/reads/pullRequests.ts +9 -4
- package/src/plus/integrations/reads/searchPullRequests.ts +12 -4
- package/src/plus/integrations/reads/sweeps.ts +1 -1
- package/src/plus/integrations/reads/warnings.ts +12 -1
- package/src/plus/integrations/results.ts +6 -1
- package/src/utils/env/browser/base64.ts +1 -1
- package/src/utils/env/node/base64.ts +3 -1
- package/src/utils/keys/chord.ts +184 -0
- package/src/utils/keys/keybinding.ts +116 -0
- package/src/utils/path.ts +14 -0
|
@@ -21,7 +21,7 @@ import type {
|
|
|
21
21
|
PullRequestState,
|
|
22
22
|
PullRequestStateFilter,
|
|
23
23
|
} from '../../../git/models/pullRequest.js';
|
|
24
|
-
import { PullRequestMergeMethod } from '../../../git/models/pullRequest.js';
|
|
24
|
+
import { defaultPullRequestSort, PullRequestMergeMethod } from '../../../git/models/pullRequest.js';
|
|
25
25
|
import type { Provider } from '../../../git/models/remoteProvider.js';
|
|
26
26
|
import type { RepositoryMetadata } from '../../../git/models/repositoryMetadata.js';
|
|
27
27
|
import type { GitRevisionRange } from '../../../git/models/revision.js';
|
|
@@ -29,6 +29,7 @@ import type { GitUser } from '../../../git/models/user.js';
|
|
|
29
29
|
import type { RepositoryVisibility } from '../../../git/providers/types.js';
|
|
30
30
|
import { getGitHubNoReplyAddressParts } from '../../../git/remotes/github.js';
|
|
31
31
|
import { effectiveIssueSort, getIssueComparator } from '../../../git/utils/issue.utils.js';
|
|
32
|
+
import { getPullRequestComparator } from '../../../git/utils/pullRequest.utils.js';
|
|
32
33
|
import {
|
|
33
34
|
createRevisionRange,
|
|
34
35
|
getRevisionRangeParts,
|
|
@@ -37,7 +38,7 @@ import {
|
|
|
37
38
|
} from '../../../git/utils/revision.utils.js';
|
|
38
39
|
import { chunk } from '../../../utils/array.js';
|
|
39
40
|
import { base64 } from '../../../utils/base64.js';
|
|
40
|
-
import { CancellationError } from '../../../utils/cancellation.js';
|
|
41
|
+
import { CancellationError, isCancellationError } from '../../../utils/cancellation.js';
|
|
41
42
|
import { trace } from '../../../utils/decorators/log.js';
|
|
42
43
|
import type { Event } from '../../../utils/event.js';
|
|
43
44
|
import { Emitter } from '../../../utils/event.js';
|
|
@@ -110,10 +111,17 @@ const accountResolveBatchSize = 25;
|
|
|
110
111
|
|
|
111
112
|
// Pull-request search selects the full PR fragment (reviews, requests, refs, commits, etc.),
|
|
112
113
|
// which makes GitHub reject broad 100-node searches with `Resource limits for this query
|
|
113
|
-
// exceeded` on large repositories. Thirty keeps the default within that GraphQL cost budget
|
|
114
|
-
//
|
|
114
|
+
// exceeded` on large repositories. Thirty keeps the default within that GraphQL cost budget;
|
|
115
|
+
// the 100-node maximum the connection accepts is for the lite shape, whose per-node cost is a
|
|
116
|
+
// fraction of it. The budget is per DOCUMENT, and one document holds every relationship × state
|
|
117
|
+
// facet, so the worst case scales with the facet count (five relationships × four states = 20).
|
|
115
118
|
const defaultPullRequestSearchPageSize = 30;
|
|
116
119
|
const maxPullRequestSearchPageSize = 100;
|
|
120
|
+
// Pages `searchMyPullRequests` drains for a caller that wants a whole list rather than a page.
|
|
121
|
+
// Four reduced pages cover more than the single 100-node page this read served before it paged,
|
|
122
|
+
// so the smaller page costs coverage nowhere; it bounds an unbounded drain on a read whose
|
|
123
|
+
// consumers (Launchpad, the Graph pull-request panel) block on it.
|
|
124
|
+
const maxMyPullRequestSearchPages = 4;
|
|
117
125
|
|
|
118
126
|
function isRetryableTransientError(ex: unknown): ex is RequestError {
|
|
119
127
|
// An aborted request is rethrown as the original `AbortError` (not a `RequestError`), so it is
|
|
@@ -223,6 +231,85 @@ title
|
|
|
223
231
|
updatedAt
|
|
224
232
|
url
|
|
225
233
|
`;
|
|
234
|
+
/**
|
|
235
|
+
* Stacked-pull-request selections, appended to a pull request's selection set rather than baked into
|
|
236
|
+
* the fragments below: GitHub Enterprise Server schemas lag github.com, and selecting `stack` there
|
|
237
|
+
* fails the entire query with "Field 'stack' doesn't exist on type 'PullRequest'" — which would take
|
|
238
|
+
* every PR feature down with it, not just stacks. Always add these via `gqlPullRequestStackFragmentFor`.
|
|
239
|
+
*/
|
|
240
|
+
const gqlPullRequestStackFragment = `
|
|
241
|
+
stack {
|
|
242
|
+
id
|
|
243
|
+
number
|
|
244
|
+
size
|
|
245
|
+
baseRefName
|
|
246
|
+
}
|
|
247
|
+
stackEntry {
|
|
248
|
+
position
|
|
249
|
+
}
|
|
250
|
+
`;
|
|
251
|
+
|
|
252
|
+
function gqlPullRequestStackFragmentFor(options?: { baseUrl?: string }): string {
|
|
253
|
+
return isGitHubDotCom(options) ? gqlPullRequestStackFragment : '';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** One layer of a stack, as returned by the stacks REST API. Ordered bottom to top. */
|
|
257
|
+
export interface GitHubStackLayer {
|
|
258
|
+
number: number;
|
|
259
|
+
state: string;
|
|
260
|
+
draft?: boolean;
|
|
261
|
+
merged_at?: string | null;
|
|
262
|
+
head: { ref: string; sha: string };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface GitHubStackResource {
|
|
266
|
+
id: string;
|
|
267
|
+
number: number;
|
|
268
|
+
/** The stack's trunk — what the bottom member targets. */
|
|
269
|
+
base: { ref: string; sha?: string };
|
|
270
|
+
/** False once every member has merged; such a stack can no longer be extended. */
|
|
271
|
+
open?: boolean;
|
|
272
|
+
/** Members ordered bottom to top. */
|
|
273
|
+
pull_requests: GitHubStackLayer[];
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Result of the asynchronous merge used for stacked pull requests. */
|
|
277
|
+
interface GitHubAsyncMergeResult {
|
|
278
|
+
status: 'pending' | 'merged' | 'enqueued' | 'failed';
|
|
279
|
+
/** Everything but `status` is nested here — flattening it silently loses the poll ticket. */
|
|
280
|
+
details?: {
|
|
281
|
+
/** Always present; on `failed` it is the only explanation of why. */
|
|
282
|
+
message?: string;
|
|
283
|
+
/** Only while `pending` — the ticket to poll. */
|
|
284
|
+
uuid?: string;
|
|
285
|
+
/** Only once `merged`. */
|
|
286
|
+
sha?: string;
|
|
287
|
+
merge_method?: string;
|
|
288
|
+
merge_action?: string;
|
|
289
|
+
expected_head_sha?: string;
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* A 409 means a merge request is already in flight and carries its ticket, so it resumes rather than fails.
|
|
295
|
+
*
|
|
296
|
+
* The body has to be dug out of `original`: `requestCore` routes every 4xx through `handleRequestError`,
|
|
297
|
+
* which re-wraps octokit's `RequestError` in a `RequestClientError` carrying only `message` and `original`.
|
|
298
|
+
*/
|
|
299
|
+
function getAsyncMergeUuidFromConflict(ex: unknown): string | undefined {
|
|
300
|
+
const err = (RequestClientError.is(ex) ? ex.original : ex) as
|
|
301
|
+
| { status?: number; response?: { data?: GitHubAsyncMergeResult } }
|
|
302
|
+
| undefined;
|
|
303
|
+
if (err?.status !== 409) return undefined;
|
|
304
|
+
|
|
305
|
+
return err.response?.data?.details?.uuid;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const asyncMergePollIntervalMs = 2000;
|
|
309
|
+
/** A stack merges one layer at a time and GitHub only promises "a few minutes", so the ceiling is
|
|
310
|
+
* generous — giving up early would report a failure for a merge still in progress. */
|
|
311
|
+
const maxAsyncMergePolls = 300;
|
|
312
|
+
|
|
226
313
|
const gqlPullRequestLiteFragment = `
|
|
227
314
|
${gqlIssueOrPullRequestFragment}
|
|
228
315
|
author {
|
|
@@ -259,6 +346,24 @@ repository {
|
|
|
259
346
|
viewerPermission
|
|
260
347
|
}
|
|
261
348
|
`;
|
|
349
|
+
/**
|
|
350
|
+
* One review's fields, shared by `latestReviews` and `viewerLatestReview`. The two selections must stay
|
|
351
|
+
* identical: `fromGitHubPullRequest` merges the viewer's review into the capped `latestReviews` window and
|
|
352
|
+
* dedups by `id`, so a field present in only one of them would produce rows that differ by where they came
|
|
353
|
+
* from rather than by what they are.
|
|
354
|
+
*/
|
|
355
|
+
const gqlPullRequestReviewFragment = `
|
|
356
|
+
id
|
|
357
|
+
author {
|
|
358
|
+
login
|
|
359
|
+
avatarUrl(size: $avatarSize)
|
|
360
|
+
url
|
|
361
|
+
}
|
|
362
|
+
state
|
|
363
|
+
commit {
|
|
364
|
+
oid
|
|
365
|
+
}
|
|
366
|
+
`;
|
|
262
367
|
const gqlPullRequestFragment = `
|
|
263
368
|
${gqlPullRequestLiteFragment}
|
|
264
369
|
additions
|
|
@@ -269,6 +374,7 @@ assignees(first: 25) {
|
|
|
269
374
|
url
|
|
270
375
|
}
|
|
271
376
|
}
|
|
377
|
+
changedFiles
|
|
272
378
|
checksUrl
|
|
273
379
|
deletions
|
|
274
380
|
mergeable
|
|
@@ -278,14 +384,12 @@ mergedBy {
|
|
|
278
384
|
reviewDecision
|
|
279
385
|
latestReviews(first: 25) {
|
|
280
386
|
nodes {
|
|
281
|
-
|
|
282
|
-
login
|
|
283
|
-
avatarUrl(size: $avatarSize)
|
|
284
|
-
url
|
|
285
|
-
}
|
|
286
|
-
state
|
|
387
|
+
${gqlPullRequestReviewFragment}
|
|
287
388
|
}
|
|
288
389
|
}
|
|
390
|
+
viewerLatestReview {
|
|
391
|
+
${gqlPullRequestReviewFragment}
|
|
392
|
+
}
|
|
289
393
|
reviewRequests(first: 25) {
|
|
290
394
|
nodes {
|
|
291
395
|
asCodeOwner
|
|
@@ -300,6 +404,7 @@ reviewRequests(first: 25) {
|
|
|
300
404
|
}
|
|
301
405
|
}
|
|
302
406
|
commits(last: 1) {
|
|
407
|
+
totalCount
|
|
303
408
|
nodes {
|
|
304
409
|
commit {
|
|
305
410
|
statusCheckRollup {
|
|
@@ -391,6 +496,7 @@ export class GitHubApi {
|
|
|
391
496
|
|
|
392
497
|
interface QueryResult {
|
|
393
498
|
viewer: {
|
|
499
|
+
databaseId: number | null;
|
|
394
500
|
name: string | null;
|
|
395
501
|
email: string | null;
|
|
396
502
|
login: string | null;
|
|
@@ -401,6 +507,7 @@ export class GitHubApi {
|
|
|
401
507
|
try {
|
|
402
508
|
const query = `query getCurrentAccount($avatarSize: Int) {
|
|
403
509
|
viewer {
|
|
510
|
+
databaseId
|
|
404
511
|
name
|
|
405
512
|
email
|
|
406
513
|
login
|
|
@@ -413,7 +520,10 @@ export class GitHubApi {
|
|
|
413
520
|
|
|
414
521
|
return {
|
|
415
522
|
provider: provider,
|
|
416
|
-
id
|
|
523
|
+
// `id` is the provider's own id, as it is for every other provider, and `username` the handle
|
|
524
|
+
// filters and viewer matching key on. Falls back to the login only when GitHub omits the
|
|
525
|
+
// database id, which leaves the two equal rather than leaving `id` empty.
|
|
526
|
+
id: rsp.viewer.databaseId != null ? String(rsp.viewer.databaseId) : rsp.viewer.login,
|
|
417
527
|
name: rsp.viewer.name ?? undefined,
|
|
418
528
|
email: rsp.viewer.email ?? undefined,
|
|
419
529
|
// If we are GitHub Enterprise, we may need to convert the avatar URL since it might require authentication
|
|
@@ -973,6 +1083,7 @@ export class GitHubApi {
|
|
|
973
1083
|
repository(name: $repo, owner: $owner) {
|
|
974
1084
|
pullRequest(number: $number) {
|
|
975
1085
|
${gqlPullRequestLiteFragment}
|
|
1086
|
+
${gqlPullRequestStackFragmentFor(options)}
|
|
976
1087
|
}
|
|
977
1088
|
}
|
|
978
1089
|
}`;
|
|
@@ -1053,6 +1164,7 @@ export class GitHubApi {
|
|
|
1053
1164
|
associatedPullRequests(first: $limit, orderBy: {field: UPDATED_AT, direction: DESC}, states: $include) {
|
|
1054
1165
|
nodes {
|
|
1055
1166
|
${gqlPullRequestLiteFragment}
|
|
1167
|
+
${gqlPullRequestStackFragmentFor(options)}
|
|
1056
1168
|
}
|
|
1057
1169
|
}
|
|
1058
1170
|
}
|
|
@@ -1146,6 +1258,7 @@ export class GitHubApi {
|
|
|
1146
1258
|
associatedPullRequests(first: 2, orderBy: {field: UPDATED_AT, direction: DESC}) {
|
|
1147
1259
|
nodes {
|
|
1148
1260
|
${gqlPullRequestLiteFragment}
|
|
1261
|
+
${gqlPullRequestStackFragmentFor(options)}
|
|
1149
1262
|
}
|
|
1150
1263
|
}
|
|
1151
1264
|
}
|
|
@@ -3283,6 +3396,40 @@ export class GitHubApi {
|
|
|
3283
3396
|
scope: ScopedLogger | undefined,
|
|
3284
3397
|
cancellation?: AbortSignal | undefined,
|
|
3285
3398
|
): Promise<Endpoints[R]['response']> {
|
|
3399
|
+
return (await this.requestCore(
|
|
3400
|
+
provider,
|
|
3401
|
+
token,
|
|
3402
|
+
route,
|
|
3403
|
+
options,
|
|
3404
|
+
scope,
|
|
3405
|
+
cancellation,
|
|
3406
|
+
)) as Endpoints[R]['response'];
|
|
3407
|
+
}
|
|
3408
|
+
|
|
3409
|
+
/**
|
|
3410
|
+
* REST call for routes `@octokit/types` doesn't describe yet — currently the stacked-pull-request
|
|
3411
|
+
* merge APIs, which are in public preview and absent from the generated endpoint map. Prefer
|
|
3412
|
+
* `request` for anything typed.
|
|
3413
|
+
*/
|
|
3414
|
+
private async requestPreview<T>(
|
|
3415
|
+
provider: Provider | undefined,
|
|
3416
|
+
token: GitHubTokenInfo,
|
|
3417
|
+
route: string,
|
|
3418
|
+
options: RequestParameters | undefined,
|
|
3419
|
+
scope: ScopedLogger | undefined,
|
|
3420
|
+
cancellation?: AbortSignal | undefined,
|
|
3421
|
+
): Promise<T> {
|
|
3422
|
+
return (await this.requestCore(provider, token, route, options, scope, cancellation)) as T;
|
|
3423
|
+
}
|
|
3424
|
+
|
|
3425
|
+
private async requestCore(
|
|
3426
|
+
provider: Provider | undefined,
|
|
3427
|
+
token: GitHubTokenInfo,
|
|
3428
|
+
route: string,
|
|
3429
|
+
options: RequestParameters | undefined,
|
|
3430
|
+
scope: ScopedLogger | undefined,
|
|
3431
|
+
cancellation?: AbortSignal | undefined,
|
|
3432
|
+
): Promise<unknown> {
|
|
3286
3433
|
const { accessToken } = token;
|
|
3287
3434
|
try {
|
|
3288
3435
|
let signal: AbortSignal | undefined;
|
|
@@ -3296,15 +3443,15 @@ export class GitHubApi {
|
|
|
3296
3443
|
// Retry transient gateway/network failures on idempotent reads only
|
|
3297
3444
|
const method = route.split(' ', 1)[0].toUpperCase();
|
|
3298
3445
|
const retryable = method === 'GET' || method === 'HEAD';
|
|
3299
|
-
return
|
|
3446
|
+
return await this.requestWithRetries(
|
|
3300
3447
|
() =>
|
|
3301
3448
|
this.config.wrapForForcedInsecureSSL(provider?.getIgnoreSSLErrors() ?? false, () =>
|
|
3302
|
-
this.getDefaults(accessToken, request)(route
|
|
3449
|
+
this.getDefaults(accessToken, request)(route, options),
|
|
3303
3450
|
),
|
|
3304
3451
|
retryable,
|
|
3305
3452
|
signal,
|
|
3306
3453
|
scope,
|
|
3307
|
-
)
|
|
3454
|
+
);
|
|
3308
3455
|
} catch (ex) {
|
|
3309
3456
|
if (ex instanceof RequestError || ex.name === 'AbortError') {
|
|
3310
3457
|
this.handleRequestError(provider, token, ex, scope);
|
|
@@ -3526,8 +3673,10 @@ export class GitHubApi {
|
|
|
3526
3673
|
* One page of the current user's pull requests, filtered by state and optionally by an explicit
|
|
3527
3674
|
* relationship qualifier. Backs the PR sweeps, which drain it page by page.
|
|
3528
3675
|
*
|
|
3529
|
-
* Ordering is part of the contract, not an option: always `sort:updated` (most recently updated first),
|
|
3530
|
-
*
|
|
3676
|
+
* Ordering is part of the contract, not an option: always `sort:updated` (most recently updated first), which is
|
|
3677
|
+
* {@link searchPullRequestsPage}'s DEFAULT rather than its only order — that read takes a `criteria.sort`, this
|
|
3678
|
+
* one does not, because a sweep's recency window is what its page budget is defined against. A caller that stops
|
|
3679
|
+
* before `hasMore` clears — every sweep with a
|
|
3531
3680
|
* page budget — therefore retains a well-defined recency window instead of an arbitrary slice of GitHub's
|
|
3532
3681
|
* relevance ranking.
|
|
3533
3682
|
*/
|
|
@@ -3558,7 +3707,15 @@ export class GitHubApi {
|
|
|
3558
3707
|
cancellation?: AbortSignal,
|
|
3559
3708
|
): Promise<{ values: PullRequest[]; cursor?: string; hasMore: boolean; truncated: boolean }> {
|
|
3560
3709
|
const scope = getScopedLogger();
|
|
3561
|
-
|
|
3710
|
+
// The page follows the projection rather than being a separate decision: the full fragment is what GitHub
|
|
3711
|
+
// rejects at 100 nodes (see `defaultPullRequestSearchPageSize`), so every read that selects it pages at the
|
|
3712
|
+
// reduced size, and only the lite shape asks for the maximum. A caller that wants a whole list pages for it
|
|
3713
|
+
// — see `searchMyPullRequests`.
|
|
3714
|
+
const configuredLimit = this.config.getLaunchpadQueryLimit?.() ?? maxPullRequestSearchPageSize;
|
|
3715
|
+
const limit = Math.min(
|
|
3716
|
+
options?.summary === true ? maxPullRequestSearchPageSize : defaultPullRequestSearchPageSize,
|
|
3717
|
+
configuredLimit,
|
|
3718
|
+
);
|
|
3562
3719
|
|
|
3563
3720
|
try {
|
|
3564
3721
|
interface SearchResult {
|
|
@@ -3586,6 +3743,7 @@ export class GitHubApi {
|
|
|
3586
3743
|
nodes {
|
|
3587
3744
|
...on PullRequest {
|
|
3588
3745
|
${options?.summary ? gqlPullRequestLiteFragment : gqlPullRequestFragment}
|
|
3746
|
+
${gqlPullRequestStackFragmentFor(options)}
|
|
3589
3747
|
}
|
|
3590
3748
|
}
|
|
3591
3749
|
}
|
|
@@ -3666,6 +3824,16 @@ export class GitHubApi {
|
|
|
3666
3824
|
}
|
|
3667
3825
|
}
|
|
3668
3826
|
|
|
3827
|
+
/**
|
|
3828
|
+
* The whole list rather than a page: this read has no cursor to hand back (its `IntegrationResult<T[]>` return
|
|
3829
|
+
* has no paging channel at all), so it drains up to {@link maxMyPullRequestSearchPages} pages itself.
|
|
3830
|
+
*
|
|
3831
|
+
* Draining instead of taking one page is what lets the full projection page at the reduced size everywhere
|
|
3832
|
+
* (see the `limit` in {@link searchMyPullRequestsPage}): a single page would otherwise have to ask for 100
|
|
3833
|
+
* nodes of the selection GitHub rejects at that size, purely because this caller cannot resume. Ordering is
|
|
3834
|
+
* `sort:updated`, so the pages compose into a defined window rather than a shifting relevance ranking, and the
|
|
3835
|
+
* drain stops as soon as GitHub reports no next page.
|
|
3836
|
+
*/
|
|
3669
3837
|
@trace({ args: (provider, token) => ({ provider: provider.name, token: `<token:${token.microHash}>` }) })
|
|
3670
3838
|
async searchMyPullRequests(
|
|
3671
3839
|
provider: Provider,
|
|
@@ -3681,7 +3849,33 @@ export class GitHubApi {
|
|
|
3681
3849
|
},
|
|
3682
3850
|
cancellation?: AbortSignal,
|
|
3683
3851
|
): Promise<PullRequest[]> {
|
|
3684
|
-
|
|
3852
|
+
const values: PullRequest[] = [];
|
|
3853
|
+
// The searches behind one page can overlap (a PR can be authored by and review-requested from the same
|
|
3854
|
+
// user), and a later page can repeat a row an earlier one already served if the PR was updated mid-drain.
|
|
3855
|
+
const seen = new Set<string>();
|
|
3856
|
+
let cursor: string | undefined;
|
|
3857
|
+
|
|
3858
|
+
for (let page = 0; page < maxMyPullRequestSearchPages; page++) {
|
|
3859
|
+
const result = await this.searchMyPullRequestsPage(
|
|
3860
|
+
provider,
|
|
3861
|
+
token,
|
|
3862
|
+
{ ...options, cursor: cursor },
|
|
3863
|
+
cancellation,
|
|
3864
|
+
);
|
|
3865
|
+
for (const pr of result.values) {
|
|
3866
|
+
if (seen.has(pr.url)) continue;
|
|
3867
|
+
|
|
3868
|
+
seen.add(pr.url);
|
|
3869
|
+
values.push(pr);
|
|
3870
|
+
}
|
|
3871
|
+
|
|
3872
|
+
// A missing or repeated cursor with `hasMore` would loop on the same page forever.
|
|
3873
|
+
if (!result.hasMore || result.cursor == null || result.cursor === cursor) break;
|
|
3874
|
+
|
|
3875
|
+
cursor = result.cursor;
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
return values;
|
|
3685
3879
|
}
|
|
3686
3880
|
|
|
3687
3881
|
/**
|
|
@@ -3948,6 +4142,85 @@ export class GitHubApi {
|
|
|
3948
4142
|
}
|
|
3949
4143
|
}
|
|
3950
4144
|
|
|
4145
|
+
/**
|
|
4146
|
+
* The PR twin of {@link countIssues}: counts pull requests for several scopes in ONE request via aliased
|
|
4147
|
+
* `search` fields selecting only `issueCount` with `first: 0`. Same positional/undefined contract as the issue
|
|
4148
|
+
* count.
|
|
4149
|
+
*
|
|
4150
|
+
* The one difference is states. {@link toGitHubPullRequestSearchFacets} fans a scope's states out into one
|
|
4151
|
+
* `search` each, so this reports the count the SAME way {@link searchPullRequestsPage} reports its total — the
|
|
4152
|
+
* LARGEST facet's `issueCount`, not their sum: the result ceiling applies per search, so the max is what
|
|
4153
|
+
* `exceedsProviderLimit` compares against, and summing would claim a total the read never surfaces. Relationships
|
|
4154
|
+
* are OR-ed and can't be a single count; the facade refuses a multi-relationship scope, so at most one is here.
|
|
4155
|
+
*/
|
|
4156
|
+
@trace({ args: (provider, token) => ({ provider: provider.name, token: `<token:${token.microHash}>` }) })
|
|
4157
|
+
async countPullRequests(
|
|
4158
|
+
provider: Provider,
|
|
4159
|
+
token: GitHubTokenInfo,
|
|
4160
|
+
scopes: readonly { repos?: string[]; org?: string; criteria?: PullRequestSearchCriteria }[],
|
|
4161
|
+
options?: { baseUrl?: string },
|
|
4162
|
+
cancellation?: AbortSignal,
|
|
4163
|
+
): Promise<(number | undefined)[]> {
|
|
4164
|
+
const scope = getScopedLogger();
|
|
4165
|
+
if (scopes.length === 0) return [];
|
|
4166
|
+
|
|
4167
|
+
// Each scope expands to one query string per state facet (all sharing its single relationship, which the
|
|
4168
|
+
// facade guarantees). A scope's count is the MAX across its facets — mirroring searchPullRequestsPage's
|
|
4169
|
+
// totalCount — so the aliases stay grouped by scope and are reduced after the response.
|
|
4170
|
+
const scopeQueries = scopes.map(s => {
|
|
4171
|
+
const scopeQualifiers = toGitHubIssueSearchScopeQualifiers(s.org, s.repos);
|
|
4172
|
+
return toGitHubPullRequestSearchFacets(s.criteria).map(f =>
|
|
4173
|
+
[...scopeQualifiers, ...f.qualifiers].join(' '),
|
|
4174
|
+
);
|
|
4175
|
+
});
|
|
4176
|
+
|
|
4177
|
+
// Aliases are positional and generated (`s${scope}f${facet}`): a caller's key is arbitrary text and would
|
|
4178
|
+
// break the document, so results are reduced back to one count per scope by index.
|
|
4179
|
+
const aliased = scopeQueries.flatMap((queries, si) =>
|
|
4180
|
+
queries.map((query, fi) => ({ alias: `s${si}f${fi}`, query: query })),
|
|
4181
|
+
);
|
|
4182
|
+
const params = aliased.map(a => `$${a.alias}: String!`).join('\n\t\t\t\t');
|
|
4183
|
+
// `first: 0` is what makes this cheap — `issueCount` alone, no nodes over the wire.
|
|
4184
|
+
const fields = aliased
|
|
4185
|
+
.map(a => `${a.alias}: search(query: $${a.alias}, type: ISSUE, first: 0) { issueCount }`)
|
|
4186
|
+
.join('\n\t\t\t\t');
|
|
4187
|
+
const query = `query countPullRequests(
|
|
4188
|
+
${params}
|
|
4189
|
+
) {
|
|
4190
|
+
${fields}
|
|
4191
|
+
}`;
|
|
4192
|
+
|
|
4193
|
+
const variables: Record<string, unknown> = { baseUrl: options?.baseUrl };
|
|
4194
|
+
for (const a of aliased) {
|
|
4195
|
+
variables[a.alias] = a.query;
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4198
|
+
try {
|
|
4199
|
+
const rsp = await this.graphql<Record<string, { issueCount?: number } | undefined>>(
|
|
4200
|
+
provider,
|
|
4201
|
+
token,
|
|
4202
|
+
query,
|
|
4203
|
+
variables,
|
|
4204
|
+
scope,
|
|
4205
|
+
cancellation,
|
|
4206
|
+
);
|
|
4207
|
+
if (rsp == null) return scopes.map(() => undefined);
|
|
4208
|
+
|
|
4209
|
+
return scopeQueries.map((queries, si) => {
|
|
4210
|
+
let max: number | undefined;
|
|
4211
|
+
for (let fi = 0; fi < queries.length; fi++) {
|
|
4212
|
+
const count = rsp[`s${si}f${fi}`]?.issueCount;
|
|
4213
|
+
if (count != null) {
|
|
4214
|
+
max = max == null ? count : Math.max(max, count);
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
return max;
|
|
4218
|
+
});
|
|
4219
|
+
} catch (ex) {
|
|
4220
|
+
throw this.handleException(ex, provider, scope);
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
|
|
3951
4224
|
/**
|
|
3952
4225
|
* The aliased-search engine behind every GitHub issue search: one GraphQL request carrying N independently
|
|
3953
4226
|
* cursored `search` fields, `@include`-gated so an exhausted or unrequested one costs nothing.
|
|
@@ -4234,8 +4507,11 @@ export class GitHubApi {
|
|
|
4234
4507
|
* every active relationship × state facet, so one HTTP request serves one page even when the logical search is
|
|
4235
4508
|
* a union. The cursor preserves each facet's continuation plus the positional page.
|
|
4236
4509
|
*
|
|
4237
|
-
* Ordering is
|
|
4238
|
-
*
|
|
4510
|
+
* Ordering is `criteria.sort`, defaulting to most-recently-updated-first — the order this read served before it
|
|
4511
|
+
* was expressible. It is requested of the provider AND re-applied to the merged page, because the page is a
|
|
4512
|
+
* union of facets and no per-facet server order describes it. The sort is part of the cursor fingerprint, so
|
|
4513
|
+
* changing it invalidates a threaded cursor exactly as changing the text or the scope does. User text is
|
|
4514
|
+
* sanitized before it reaches the provider query. See {@link toGitHubPullRequestSearchFacets}.
|
|
4239
4515
|
*/
|
|
4240
4516
|
@trace({ args: (provider, token) => ({ provider: provider.name, token: `<token:${token.microHash}>` }) })
|
|
4241
4517
|
async searchPullRequestsPage(
|
|
@@ -4351,6 +4627,7 @@ export class GitHubApi {
|
|
|
4351
4627
|
nodes {
|
|
4352
4628
|
... on PullRequest {
|
|
4353
4629
|
${gqlPullRequestFragment}
|
|
4630
|
+
${gqlPullRequestStackFragmentFor(options)}
|
|
4354
4631
|
}
|
|
4355
4632
|
}
|
|
4356
4633
|
}`,
|
|
@@ -4395,7 +4672,15 @@ export class GitHubApi {
|
|
|
4395
4672
|
}
|
|
4396
4673
|
}
|
|
4397
4674
|
}
|
|
4398
|
-
|
|
4675
|
+
// The PR path re-sorts the merged page rather than trusting the per-facet server order — GitHub's
|
|
4676
|
+
// server-side PR sort has been unreliable (the per-branch path re-sorts for the same reason), and the
|
|
4677
|
+
// union of several facets is unordered regardless. The key was validated against
|
|
4678
|
+
// `githubPullRequestSearchCapabilities.sorts` upstream, so its comparator is always defined for a PR
|
|
4679
|
+
// shape; the guard only guards the unreachable case rather than inventing an order for it.
|
|
4680
|
+
const comparator = getPullRequestComparator(options?.criteria?.sort ?? defaultPullRequestSort);
|
|
4681
|
+
if (comparator != null) {
|
|
4682
|
+
pullRequests.sort(comparator);
|
|
4683
|
+
}
|
|
4399
4684
|
const values = [
|
|
4400
4685
|
...uniqueBy(
|
|
4401
4686
|
pullRequests,
|
|
@@ -4495,6 +4780,7 @@ export class GitHubApi {
|
|
|
4495
4780
|
nodes {
|
|
4496
4781
|
...on PullRequest {
|
|
4497
4782
|
${gqlPullRequestFragment}
|
|
4783
|
+
${gqlPullRequestStackFragmentFor(options)}
|
|
4498
4784
|
}
|
|
4499
4785
|
}
|
|
4500
4786
|
}
|
|
@@ -4554,6 +4840,164 @@ export class GitHubApi {
|
|
|
4554
4840
|
}
|
|
4555
4841
|
}
|
|
4556
4842
|
|
|
4843
|
+
/**
|
|
4844
|
+
* Every stack in the repository, each with its members bottom to top.
|
|
4845
|
+
*
|
|
4846
|
+
* One request regardless of how many pull requests are involved — which is what makes it the right
|
|
4847
|
+
* shape for list surfaces, whose pull requests arrive through the shared providers API and so carry no
|
|
4848
|
+
* stack membership of their own. Callers join the result by pull request number.
|
|
4849
|
+
*
|
|
4850
|
+
* A `404` means the repository isn't enrolled in the stacked-pull-requests preview; that's reported as
|
|
4851
|
+
* `undefined` rather than an error, since "no stacks" and "not available" both mean nothing to show.
|
|
4852
|
+
*/
|
|
4853
|
+
@trace({
|
|
4854
|
+
args: (provider, token, owner, repo) => ({
|
|
4855
|
+
provider: provider.name,
|
|
4856
|
+
token: `<token:${token.microHash}>`,
|
|
4857
|
+
owner: owner,
|
|
4858
|
+
repo: repo,
|
|
4859
|
+
}),
|
|
4860
|
+
})
|
|
4861
|
+
async getRepositoryStacks(
|
|
4862
|
+
provider: Provider,
|
|
4863
|
+
token: GitHubTokenInfo,
|
|
4864
|
+
owner: string,
|
|
4865
|
+
repo: string,
|
|
4866
|
+
options?: { baseUrl?: string },
|
|
4867
|
+
cancellation?: AbortSignal,
|
|
4868
|
+
): Promise<GitHubStackResource[] | undefined> {
|
|
4869
|
+
const scope = getScopedLogger();
|
|
4870
|
+
|
|
4871
|
+
try {
|
|
4872
|
+
const rsp = await this.requestPreview<{ data: GitHubStackResource[] }>(
|
|
4873
|
+
provider,
|
|
4874
|
+
token,
|
|
4875
|
+
'GET /repos/{owner}/{repo}/stacks',
|
|
4876
|
+
{ owner: owner, repo: repo, baseUrl: options?.baseUrl },
|
|
4877
|
+
scope,
|
|
4878
|
+
cancellation,
|
|
4879
|
+
);
|
|
4880
|
+
|
|
4881
|
+
return rsp.data;
|
|
4882
|
+
} catch (ex) {
|
|
4883
|
+
if (isCancellationError(ex)) throw ex;
|
|
4884
|
+
|
|
4885
|
+
Logger.warn(scope, `Unable to list stacks for ${owner}/${repo}: ${ex}`);
|
|
4886
|
+
return undefined;
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
|
|
4890
|
+
/**
|
|
4891
|
+
* Merges a stacked pull request, and with it every layer below it.
|
|
4892
|
+
*
|
|
4893
|
+
* Stacks cannot go through `mergePullRequest` — GitHub rejects stacked pull requests on the legacy
|
|
4894
|
+
* synchronous merge endpoints and mutations. The replacement is asynchronous: submit, then poll a
|
|
4895
|
+
* ticket until it reaches a terminal state.
|
|
4896
|
+
*/
|
|
4897
|
+
@trace({
|
|
4898
|
+
args: (provider, token, owner, repo, pullNumber, expectedSourceSha) => ({
|
|
4899
|
+
provider: provider.name,
|
|
4900
|
+
token: `<token:${token.microHash}>`,
|
|
4901
|
+
owner: owner,
|
|
4902
|
+
repo: repo,
|
|
4903
|
+
pullNumber: pullNumber,
|
|
4904
|
+
expectedSourceSha: expectedSourceSha,
|
|
4905
|
+
}),
|
|
4906
|
+
})
|
|
4907
|
+
async mergeStackedPullRequest(
|
|
4908
|
+
provider: Provider,
|
|
4909
|
+
token: GitHubTokenInfo,
|
|
4910
|
+
owner: string,
|
|
4911
|
+
repo: string,
|
|
4912
|
+
pullNumber: number,
|
|
4913
|
+
expectedSourceSha: string,
|
|
4914
|
+
options?: { mergeMethod?: PullRequestMergeMethod; baseUrl?: string },
|
|
4915
|
+
cancellation?: AbortSignal,
|
|
4916
|
+
): Promise<boolean> {
|
|
4917
|
+
const scope = getScopedLogger();
|
|
4918
|
+
|
|
4919
|
+
try {
|
|
4920
|
+
let uuid: string | undefined;
|
|
4921
|
+
|
|
4922
|
+
try {
|
|
4923
|
+
const submitted = await this.requestPreview<{ data: GitHubAsyncMergeResult }>(
|
|
4924
|
+
provider,
|
|
4925
|
+
token,
|
|
4926
|
+
'PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge-async',
|
|
4927
|
+
{
|
|
4928
|
+
owner: owner,
|
|
4929
|
+
repo: repo,
|
|
4930
|
+
pull_number: pullNumber,
|
|
4931
|
+
sha: expectedSourceSha,
|
|
4932
|
+
merge_method: options?.mergeMethod,
|
|
4933
|
+
baseUrl: options?.baseUrl,
|
|
4934
|
+
},
|
|
4935
|
+
scope,
|
|
4936
|
+
cancellation,
|
|
4937
|
+
);
|
|
4938
|
+
|
|
4939
|
+
// Already merged or rejected outright — nothing to poll for. `enqueued` lands via the merge
|
|
4940
|
+
// queue, which can take several merge groups to settle, so it's transitional like `pending`.
|
|
4941
|
+
if (submitted.data.status !== 'pending' && submitted.data.status !== 'enqueued') {
|
|
4942
|
+
if (submitted.data.status === 'failed') {
|
|
4943
|
+
Logger.warn(
|
|
4944
|
+
scope,
|
|
4945
|
+
`Stacked merge refused: ${submitted.data.details?.message ?? 'no reason given'}`,
|
|
4946
|
+
);
|
|
4947
|
+
}
|
|
4948
|
+
return submitted.data.status === 'merged';
|
|
4949
|
+
}
|
|
4950
|
+
|
|
4951
|
+
uuid = submitted.data.details?.uuid;
|
|
4952
|
+
} catch (ex) {
|
|
4953
|
+
// A merge request is already in flight (a retry, or a double-click) — adopt its ticket and
|
|
4954
|
+
// poll that instead of reporting a failure for a merge that is actually running.
|
|
4955
|
+
uuid = getAsyncMergeUuidFromConflict(ex);
|
|
4956
|
+
if (uuid == null) throw ex;
|
|
4957
|
+
}
|
|
4958
|
+
|
|
4959
|
+
if (uuid == null) return false;
|
|
4960
|
+
|
|
4961
|
+
// The stack merges server-side one layer at a time, so this can take a while. Poll on a
|
|
4962
|
+
// fixed interval and give up rather than hang forever if the ticket never settles.
|
|
4963
|
+
for (let attempt = 0; attempt < maxAsyncMergePolls; attempt++) {
|
|
4964
|
+
if (cancellation?.aborted) throw new CancellationError();
|
|
4965
|
+
|
|
4966
|
+
await new Promise(resolve => setTimeout(resolve, asyncMergePollIntervalMs));
|
|
4967
|
+
|
|
4968
|
+
const polled = await this.requestPreview<{ data: GitHubAsyncMergeResult }>(
|
|
4969
|
+
provider,
|
|
4970
|
+
token,
|
|
4971
|
+
'GET /repos/{owner}/{repo}/pulls/{pull_number}/merge-async/{uuid}',
|
|
4972
|
+
{
|
|
4973
|
+
owner: owner,
|
|
4974
|
+
repo: repo,
|
|
4975
|
+
pull_number: pullNumber,
|
|
4976
|
+
uuid: uuid,
|
|
4977
|
+
baseUrl: options?.baseUrl,
|
|
4978
|
+
},
|
|
4979
|
+
scope,
|
|
4980
|
+
cancellation,
|
|
4981
|
+
);
|
|
4982
|
+
|
|
4983
|
+
if (polled.data.status === 'pending' || polled.data.status === 'enqueued') continue;
|
|
4984
|
+
|
|
4985
|
+
if (polled.data.status === 'failed') {
|
|
4986
|
+
Logger.warn(scope, `Stacked merge failed: ${polled.data.details?.message ?? 'no reason given'}`);
|
|
4987
|
+
}
|
|
4988
|
+
return polled.data.status === 'merged';
|
|
4989
|
+
}
|
|
4990
|
+
|
|
4991
|
+
Logger.warn(scope, `Timed out waiting for stacked merge of ${owner}/${repo}#${pullNumber}`);
|
|
4992
|
+
return false;
|
|
4993
|
+
} catch (ex) {
|
|
4994
|
+
if (isCancellationError(ex)) throw ex;
|
|
4995
|
+
|
|
4996
|
+
Logger.error(ex, scope);
|
|
4997
|
+
throw this.handleException(ex, provider, scope);
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
|
|
4557
5001
|
@trace({
|
|
4558
5002
|
args: (provider, token, nodeId, expectedSourceSha) => ({
|
|
4559
5003
|
provider: provider.name,
|
|
@@ -4572,7 +5016,7 @@ export class GitHubApi {
|
|
|
4572
5016
|
): Promise<boolean> {
|
|
4573
5017
|
const scope = getScopedLogger();
|
|
4574
5018
|
interface QueryResult {
|
|
4575
|
-
pullRequest:
|
|
5019
|
+
mergePullRequest: { pullRequest: { id: string } | null | undefined } | null | undefined;
|
|
4576
5020
|
}
|
|
4577
5021
|
|
|
4578
5022
|
let githubMergeStrategy;
|
|
@@ -4620,7 +5064,7 @@ export class GitHubApi {
|
|
|
4620
5064
|
cancellation,
|
|
4621
5065
|
);
|
|
4622
5066
|
|
|
4623
|
-
return rsp?.pullRequest?.id === nodeId;
|
|
5067
|
+
return rsp?.mergePullRequest?.pullRequest?.id === nodeId;
|
|
4624
5068
|
} catch (ex) {
|
|
4625
5069
|
throw this.handleException(ex, provider, scope);
|
|
4626
5070
|
}
|