mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-21 14:07:20 +08:00
fix(jetbrains): address agent manager review feedback
This commit is contained in:
@@ -2,4 +2,4 @@
|
||||
"@kilocode/kilo-jetbrains": minor
|
||||
---
|
||||
|
||||
Add a Terminal button to the Agent Manager worktree header that opens (or focuses) a terminal in the worktree's directory, reusing one terminal tab per worktree. The terminal tab is labelled with the same worktree name shown in the worktree list and editor tab, and it updates when the worktree is renamed or its pull request changes. The worktree header actions now use flat, hoverable toolbar buttons, and the branch-changes badge shows the commits-ahead count and file count with a descriptive tooltip (ahead commits, files changed, and lines added/removed).
|
||||
Add a Terminal button to the Agent Manager worktree header that opens (or focuses) a terminal in the worktree's directory, reusing one terminal tab per worktree. The terminal tab is labelled with the same worktree name shown in the worktree list and editor tab, and it updates when the worktree is renamed or its pull request changes. The worktree header actions now use flat, hoverable toolbar buttons, and the branch-changes badge shows the changed-file count and lines added/removed, opening the branch diff when clicked.
|
||||
|
||||
+7
-7
@@ -165,12 +165,9 @@ class KiloWorktreeRpcApiImpl : KiloWorktreeRpcApi {
|
||||
if (status != GhAvailability.OK) return@parallel null
|
||||
val out = runGh(Path.of(item.path).normalize(), "pr", "view", item.branch, "--json", "number,state,isDraft,url,title")
|
||||
if (!out.ok) {
|
||||
when (prError(out.stderr)) {
|
||||
GhAvailability.UNAUTH -> status = GhAvailability.UNAUTH
|
||||
GhAvailability.MISSING -> status = GhAvailability.MISSING
|
||||
GhAvailability.GIT_MISSING -> status = GhAvailability.GIT_MISSING
|
||||
GhAvailability.OK -> Unit
|
||||
}
|
||||
// prError only ever returns UNAUTH or OK; a missing gh/git binary is already caught
|
||||
// by the upfront ghAvailable() check before this loop runs.
|
||||
if (prError(out.stderr) == GhAvailability.UNAUTH) status = GhAvailability.UNAUTH
|
||||
return@parallel null
|
||||
}
|
||||
parsePr(item.path, out.stdout)
|
||||
@@ -457,7 +454,10 @@ class KiloWorktreeRpcApiImpl : KiloWorktreeRpcApi {
|
||||
internal fun classifyGhError(text: String): GhAvailability {
|
||||
val msg = text.lowercase()
|
||||
if (msg.contains("not logged") || msg.contains("gh auth login") || msg.contains("authentication")) return GhAvailability.UNAUTH
|
||||
if (msg.contains("cannot run program") || msg.contains("no such file") || msg.contains("not found")) return GhAvailability.MISSING
|
||||
// Only treat process-spawn failures as MISSING. A bare "not found" match would misclassify
|
||||
// transient gh auth failures (e.g. a GitHub Enterprise 404 or revoked token) as an uninstalled gh;
|
||||
// scope to spawn/shell signals instead.
|
||||
if (msg.contains("cannot run program") || msg.contains("no such file") || msg.contains("command not found")) return GhAvailability.MISSING
|
||||
return GhAvailability.OK
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -167,5 +167,8 @@ internal fun Content.applyAgentManagerBetaBadge() {
|
||||
icon = AllIcons.General.Beta
|
||||
description = KiloBundle.message("sidePanel.mode.agentManager.beta.description")
|
||||
putUserData(ToolWindow.SHOW_CONTENT_ICON, true)
|
||||
// TAB_LABEL_ORIENTATION_KEY is @ApiStatus.Experimental and may change or disappear between IDE
|
||||
// releases; we declare no untilBuild cap. Failure is benign: putUserData no-ops and the Beta
|
||||
// icon falls back to the left of the tab label.
|
||||
putUserData(Content.TAB_LABEL_ORIENTATION_KEY, ComponentOrientation.RIGHT_TO_LEFT)
|
||||
}
|
||||
|
||||
+19
-6
@@ -17,6 +17,7 @@ import com.intellij.openapi.components.Service
|
||||
import fleet.rpc.client.durable
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* App-level service wrapping [ai.kilocode.rpc.KiloWorktreeRpcApi]. Mirrors [ai.kilocode.client.app.KiloWorkspaceService]:
|
||||
@@ -63,6 +64,18 @@ class KiloWorktreeService internal constructor(
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire-and-forget open on the service scope. EDT click handlers have no thread-bound coroutine
|
||||
* scope, so they route through here instead of `currentThreadCoroutineScope()`, which throws
|
||||
* outside progress/blocking contexts.
|
||||
*/
|
||||
fun openInBackground(directory: String) {
|
||||
cs.launch {
|
||||
val ok = open(directory)
|
||||
LOG.info("worktree open: backend returned=$ok dir=$directory")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun stats(directory: String): WorktreeStatsListDto = try {
|
||||
call { stats(directory) }
|
||||
} catch (e: Exception) {
|
||||
@@ -70,12 +83,12 @@ class KiloWorktreeService internal constructor(
|
||||
WorktreeStatsListDto()
|
||||
}
|
||||
|
||||
suspend fun ghStatus(directory: String): GhAvailability = try {
|
||||
call { ghStatus(directory) }
|
||||
} catch (e: Exception) {
|
||||
LOG.warn("gh status failed for $directory", e)
|
||||
GhAvailability.OK
|
||||
}
|
||||
/**
|
||||
* Reports gh availability, or rethrows on RPC/backend failure. Callers ([GhStatusCoordinator])
|
||||
* distinguish a healthy gh from an unhealthy backend via their own `runCatching` + backoff;
|
||||
* swallowing errors here would publish a false "gh is fine" and reset that backoff.
|
||||
*/
|
||||
suspend fun ghStatus(directory: String): GhAvailability = call { ghStatus(directory) }
|
||||
|
||||
suspend fun prStatus(directory: String): WorktreePrListDto = try {
|
||||
call { prStatus(directory) }
|
||||
|
||||
+10
-11
@@ -31,6 +31,7 @@ import ai.kilocode.rpc.dto.SessionDto
|
||||
import ai.kilocode.rpc.dto.WorktreePrDto
|
||||
import ai.kilocode.rpc.dto.WorktreeStatsDto
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.ActivityTracker
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
@@ -43,7 +44,6 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.openapi.actionSystem.UiDataProvider
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.progress.currentThreadCoroutineScope
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.util.Disposer
|
||||
@@ -61,8 +61,6 @@ import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.plugins.terminal.TerminalToolWindowFactory
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Color
|
||||
@@ -266,9 +264,14 @@ class WorktreeSessionEditorPanel(
|
||||
|
||||
@RequiresEdt
|
||||
private fun syncToolbar() {
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) return
|
||||
@Suppress("DEPRECATION")
|
||||
toolbar.updateActionsImmediately()
|
||||
// Tests need a synchronous refresh to assert action presentations; production nudges the
|
||||
// platform's action-update pass instead of the deprecated blocking updateActionsImmediately().
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
@Suppress("DEPRECATION")
|
||||
toolbar.updateActionsImmediately()
|
||||
return
|
||||
}
|
||||
ActivityTracker.getInstance().inc()
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
@@ -292,11 +295,7 @@ class WorktreeSessionEditorPanel(
|
||||
}
|
||||
if (focusExistingFrame(dir)) return
|
||||
LOG.info("worktree open: no local frame matched, delegating to backend dir=$dir")
|
||||
currentThreadCoroutineScope().launch(Dispatchers.Default) {
|
||||
val focused = runCatching { service<KiloWorktreeService>().open(dir) }
|
||||
focused.onFailure { LOG.warn("worktree open: backend call failed dir=$dir", it) }
|
||||
focused.onSuccess { LOG.info("worktree open: backend returned=$it dir=$dir") }
|
||||
}
|
||||
service<KiloWorktreeService>().openInBackground(dir)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -393,7 +393,6 @@ worktree.pr.state.closed=Closed
|
||||
worktree.pr.tooltip.open=Click to open the pull request in your browser.
|
||||
worktree.gh.missing.title=GitHub CLI not found
|
||||
worktree.gh.missing.content=Install gh to show pull request badges for worktrees.
|
||||
worktree.gh.install=Install
|
||||
worktree.git.missing.title=Git not found
|
||||
worktree.git.missing.content=Install Git to show worktree stats and pull request badges.
|
||||
worktree.gh.unauth.title=GitHub CLI not authorized
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=لا يمكن حذف جلسات السحابة بعد
|
||||
|
||||
worktree.gh.missing.title=لم يتم العثور على GitHub CLI
|
||||
worktree.gh.missing.content=ثبّت gh لعرض شارات طلبات السحب لفروع العمل.
|
||||
worktree.gh.install=تثبيت
|
||||
worktree.git.missing.title=لم يتم العثور على Git
|
||||
worktree.git.missing.content=ثبّت Git لعرض إحصاءات فروع العمل وشارات طلبات السحب.
|
||||
worktree.gh.unauth.title=GitHub CLI غير مخوّل
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=Sesije iz oblaka još uvijek nije moguće brisati
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI nije pronađen
|
||||
worktree.gh.missing.content=Instalirajte gh za prikaz oznaka pull requestova za radna stabla.
|
||||
worktree.gh.install=Instaliraj
|
||||
worktree.git.missing.title=Git nije pronađen
|
||||
worktree.git.missing.content=Instalirajte Git za prikaz statistike radnih stabala i oznaka pull requestova.
|
||||
worktree.gh.unauth.title=GitHub CLI nije autorizovan
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=Skysessioner kan endnu ikke slettes
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI blev ikke fundet
|
||||
worktree.gh.missing.content=Installer gh for at vise pull request-badges for worktrees.
|
||||
worktree.gh.install=Installer
|
||||
worktree.git.missing.title=Git blev ikke fundet
|
||||
worktree.git.missing.content=Installer Git for at vise worktree-statistik og pull request-badges.
|
||||
worktree.gh.unauth.title=GitHub CLI er ikke autoriseret
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=Cloud-Sitzungen können noch nicht gelöscht werden
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI nicht gefunden
|
||||
worktree.gh.missing.content=Installiere gh, um Pull-Request-Badges für Worktrees anzuzeigen.
|
||||
worktree.gh.install=Installieren
|
||||
worktree.git.missing.title=Git nicht gefunden
|
||||
worktree.git.missing.content=Installiere Git, um Worktree-Statistiken und Pull-Request-Badges anzuzeigen.
|
||||
worktree.gh.unauth.title=GitHub CLI ist nicht autorisiert
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=Las sesiones en la nube no se pueden eliminar aún
|
||||
|
||||
worktree.gh.missing.title=No se encontró GitHub CLI
|
||||
worktree.gh.missing.content=Instala gh para mostrar insignias de pull request para los worktrees.
|
||||
worktree.gh.install=Instalar
|
||||
worktree.git.missing.title=No se encontró Git
|
||||
worktree.git.missing.content=Instala Git para mostrar estadísticas de worktrees e insignias de pull request.
|
||||
worktree.gh.unauth.title=GitHub CLI no está autorizado
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=Les sessions cloud ne peuvent pas encore être suppri
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI introuvable
|
||||
worktree.gh.missing.content=Installez gh pour afficher les badges de pull request des worktrees.
|
||||
worktree.gh.install=Installer
|
||||
worktree.git.missing.title=Git introuvable
|
||||
worktree.git.missing.content=Installez Git pour afficher les statistiques des worktrees et les badges de pull request.
|
||||
worktree.gh.unauth.title=GitHub CLI n’est pas autorisé
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=クラウドセッションはまだ削除できま
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI が見つかりません
|
||||
worktree.gh.missing.content=worktree のプルリクエストバッジを表示するには gh をインストールしてください。
|
||||
worktree.gh.install=インストール
|
||||
worktree.git.missing.title=Git が見つかりません
|
||||
worktree.git.missing.content=worktree の統計情報とプルリクエストバッジを表示するには Git をインストールしてください。
|
||||
worktree.gh.unauth.title=GitHub CLI が認証されていません
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=클라우드 세션은 아직 삭제할 수 없습니
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI를 찾을 수 없음
|
||||
worktree.gh.missing.content=워크트리의 pull request 배지를 표시하려면 gh를 설치하세요.
|
||||
worktree.gh.install=설치
|
||||
worktree.git.missing.title=Git을 찾을 수 없음
|
||||
worktree.git.missing.content=워크트리 통계와 pull request 배지를 표시하려면 Git을 설치하세요.
|
||||
worktree.gh.unauth.title=GitHub CLI가 인증되지 않음
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=Cloudsessies kunnen nog niet worden verwijderd
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI niet gevonden
|
||||
worktree.gh.missing.content=Installeer gh om pull request-badges voor worktrees te tonen.
|
||||
worktree.gh.install=Installeren
|
||||
worktree.git.missing.title=Git niet gevonden
|
||||
worktree.git.missing.content=Installeer Git om worktree-statistieken en pull request-badges te tonen.
|
||||
worktree.gh.unauth.title=GitHub CLI is niet geautoriseerd
|
||||
|
||||
@@ -144,7 +144,6 @@ history.error.cloud.delete=Sky-økter kan ikke slettes ennå
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI ble ikke funnet
|
||||
worktree.gh.missing.content=Installer gh for å vise pull request-merker for worktrees.
|
||||
worktree.gh.install=Installer
|
||||
worktree.git.missing.title=Git ble ikke funnet
|
||||
worktree.git.missing.content=Installer Git for å vise worktree-statistikk og pull request-merker.
|
||||
worktree.gh.unauth.title=GitHub CLI er ikke autorisert
|
||||
|
||||
@@ -144,7 +144,6 @@ history.error.cloud.delete=Sesji w chmurze nie można jeszcze usuwać
|
||||
|
||||
worktree.gh.missing.title=Nie znaleziono GitHub CLI
|
||||
worktree.gh.missing.content=Zainstaluj gh, aby wyświetlać odznaki pull requestów dla worktree.
|
||||
worktree.gh.install=Zainstaluj
|
||||
worktree.git.missing.title=Nie znaleziono Git
|
||||
worktree.git.missing.content=Zainstaluj Git, aby wyświetlać statystyki worktree i odznaki pull requestów.
|
||||
worktree.gh.unauth.title=GitHub CLI nie jest autoryzowany
|
||||
|
||||
-1
@@ -144,7 +144,6 @@ history.error.cloud.delete=As sessões na nuvem ainda não podem ser excluídas
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI não encontrado
|
||||
worktree.gh.missing.content=Instale o gh para mostrar emblemas de pull request para worktrees.
|
||||
worktree.gh.install=Instalar
|
||||
worktree.git.missing.title=Git não encontrado
|
||||
worktree.git.missing.content=Instale o Git para mostrar estatísticas de worktrees e emblemas de pull request.
|
||||
worktree.gh.unauth.title=GitHub CLI não autorizado
|
||||
|
||||
@@ -144,7 +144,6 @@ history.error.cloud.delete=Облачные сессии пока нельзя
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI не найден
|
||||
worktree.gh.missing.content=Установите gh, чтобы показывать значки pull request для worktree.
|
||||
worktree.gh.install=Установить
|
||||
worktree.git.missing.title=Git не найден
|
||||
worktree.git.missing.content=Установите Git, чтобы показывать статистику worktree и значки pull request.
|
||||
worktree.gh.unauth.title=GitHub CLI не авторизован
|
||||
|
||||
@@ -144,7 +144,6 @@ history.error.cloud.delete=ยังไม่สามารถลบเซส
|
||||
|
||||
worktree.gh.missing.title=ไม่พบ GitHub CLI
|
||||
worktree.gh.missing.content=ติดตั้ง gh เพื่อแสดงป้าย pull request สำหรับ worktree
|
||||
worktree.gh.install=ติดตั้ง
|
||||
worktree.git.missing.title=ไม่พบ Git
|
||||
worktree.git.missing.content=ติดตั้ง Git เพื่อแสดงสถิติ worktree และป้าย pull request
|
||||
worktree.gh.unauth.title=GitHub CLI ยังไม่ได้รับอนุญาต
|
||||
|
||||
@@ -144,7 +144,6 @@ history.error.cloud.delete=Bulut oturumları henüz silinemiyor
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI bulunamadı
|
||||
worktree.gh.missing.content=Worktree pull request rozetlerini göstermek için gh yükleyin.
|
||||
worktree.gh.install=Yükle
|
||||
worktree.git.missing.title=Git bulunamadı
|
||||
worktree.git.missing.content=Worktree istatistiklerini ve pull request rozetlerini göstermek için Git yükleyin.
|
||||
worktree.gh.unauth.title=GitHub CLI yetkilendirilmemiş
|
||||
|
||||
@@ -139,7 +139,6 @@ history.error.cloud.delete=Хмарні сесії поки не можна ви
|
||||
|
||||
worktree.gh.missing.title=GitHub CLI не знайдено
|
||||
worktree.gh.missing.content=Установіть gh, щоб показувати значки pull request для worktree.
|
||||
worktree.gh.install=Установити
|
||||
worktree.git.missing.title=Git не знайдено
|
||||
worktree.git.missing.content=Установіть Git, щоб показувати статистику worktree і значки pull request.
|
||||
worktree.gh.unauth.title=GitHub CLI не авторизовано
|
||||
|
||||
-1
@@ -139,7 +139,6 @@ history.error.cloud.delete=云端会话暂无法删除
|
||||
|
||||
worktree.gh.missing.title=未找到 GitHub CLI
|
||||
worktree.gh.missing.content=安装 gh 以显示 worktree 的 pull request 徽章。
|
||||
worktree.gh.install=安装
|
||||
worktree.git.missing.title=未找到 Git
|
||||
worktree.git.missing.content=安装 Git 以显示 worktree 统计信息和 pull request 徽章。
|
||||
worktree.gh.unauth.title=GitHub CLI 未授权
|
||||
|
||||
-1
@@ -139,7 +139,6 @@ history.error.cloud.delete=雲端工作階段暫時無法刪除
|
||||
|
||||
worktree.gh.missing.title=找不到 GitHub CLI
|
||||
worktree.gh.missing.content=安裝 gh 以顯示 worktree 的 pull request 徽章。
|
||||
worktree.gh.install=安裝
|
||||
worktree.git.missing.title=找不到 Git
|
||||
worktree.git.missing.content=安裝 Git 以顯示 worktree 統計資料和 pull request 徽章。
|
||||
worktree.gh.unauth.title=GitHub CLI 未授權
|
||||
|
||||
+22
@@ -79,6 +79,28 @@ class GhStatusCoordinatorTest : BasePlatformTestCase() {
|
||||
handle.close()
|
||||
}
|
||||
|
||||
fun `test coordinator backs off on backend failure without reporting ok`() {
|
||||
rpc.ghResult = GhAvailability.UNAUTH
|
||||
val handle = edtWait { service.attach(project) }
|
||||
drain()
|
||||
assertEquals(GhAvailability.UNAUTH, service.current())
|
||||
assertEquals(1, rpc.ghCalls.size)
|
||||
|
||||
// A backend/RPC failure must reach the coordinator's failure path, not be laundered into OK.
|
||||
rpc.beforeGhStatus = { throw RuntimeException("backend down") }
|
||||
timers.advanceBy(5_000)
|
||||
drain()
|
||||
assertEquals(2, rpc.ghCalls.size)
|
||||
assertEquals(GhAvailability.UNAUTH, service.current())
|
||||
|
||||
// failures>0 now drives exponential backoff instead of the steady FAST cadence.
|
||||
timers.advanceBy(5_000)
|
||||
drain()
|
||||
assertEquals(3, rpc.ghCalls.size)
|
||||
assertEquals(GhAvailability.UNAUTH, service.current())
|
||||
handle.close()
|
||||
}
|
||||
|
||||
fun `test coordinator stops polling after detach`() {
|
||||
val handle = edtWait { service.attach(project) }
|
||||
drain()
|
||||
|
||||
Reference in New Issue
Block a user