mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-21 05:52:35 +08:00
Merge pull request #13369 from Kilo-Org/migrate-settings-to-vscode-multiproject-support
feat(vscode): add project-scoped Agent Manager settings tab
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Move Agent Manager worktree settings into the Kilo Settings editor and add project selection for multi-project workspaces.
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:82b91b6fcb81a7500e37dfcc335d2977955cab4f786efa30f35880c09cd8987c
|
||||
size 34732
|
||||
oid sha256:e9b217741df7ecac772898be685b192360d65c8709fce6ff65c133aba50715e2
|
||||
size 34695
|
||||
|
||||
@@ -37,6 +37,7 @@ import { spawn } from "node:child_process"
|
||||
const win = process.platform === "win32"
|
||||
const root = join(import.meta.dir, "..")
|
||||
const repo = resolve(root, "..", "..")
|
||||
const temp = tmpdir().trimEnd()
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Argument parsing
|
||||
@@ -102,7 +103,7 @@ const base =
|
||||
? expand(opts["state-dir"])
|
||||
: isolated
|
||||
? join(dev, "vscode")
|
||||
: join(tmpdir(), `kilo-vscode-dev-${hash}`)
|
||||
: join(temp, `kilo-vscode-dev-${hash}`)
|
||||
const userDir = join(base, "user-data")
|
||||
const extDir = join(base, "extensions")
|
||||
const kilo =
|
||||
|
||||
@@ -444,6 +444,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
private ignoreControllerDir: string | null = null
|
||||
private chatAutocomplete: ChatTextAreaAutocomplete | null = null
|
||||
private projectDirectory: string | null | undefined
|
||||
private settingsGeneration = 0
|
||||
private indexingProjectId: string | undefined
|
||||
private indexingSettingsRequest = 0
|
||||
private indexingStatusRequest = 0
|
||||
@@ -1048,6 +1049,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
return
|
||||
}
|
||||
if (this.handleEditorOpenMessage(message)) return
|
||||
if (await this.handleAgentManagerSettingsMessage(message)) return
|
||||
if (
|
||||
await handleWorkStyleMessage({
|
||||
message,
|
||||
@@ -1201,7 +1203,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
}
|
||||
break
|
||||
case "openSettingsPanel":
|
||||
vscode.commands.executeCommand("kilo-code.new.settingsButtonClicked", message.tab)
|
||||
vscode.commands.executeCommand("kilo-code.new.settingsButtonClicked", message.tab, message.projectId)
|
||||
break
|
||||
case "openKiloClaw":
|
||||
vscode.commands.executeCommand("kilo-code.new.kiloClawOpen")
|
||||
@@ -3714,6 +3716,94 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
return this.projectDirectory ?? this.getRootDirectory()
|
||||
}
|
||||
|
||||
private async handleAgentManagerSettingsMessage(
|
||||
message: TypedWebviewMessage & { projectId?: string; branch?: string; requestId?: string },
|
||||
): Promise<boolean> {
|
||||
const handler = this.opts.agentManagerSettings
|
||||
if (!handler) return false
|
||||
const requestId = message.requestId
|
||||
if (typeof requestId !== "string") return false
|
||||
// Only Agent Manager settings messages participate in the generation
|
||||
// guard: unrelated Settings-panel requests also carry requestId and must
|
||||
// not invalidate an in-flight projects/branches response.
|
||||
if (
|
||||
message.type !== "requestAgentManagerSettings" &&
|
||||
message.type !== "requestAgentManagerSettingsBranches" &&
|
||||
message.type !== "setAgentManagerDefaultBaseBranch" &&
|
||||
message.type !== "configureAgentManagerSetupScript"
|
||||
)
|
||||
return false
|
||||
// The settings handler is project-scoped by projectId; the panel's own
|
||||
// project directory (config bindings, saves, local-config opens) must
|
||||
// stay untouched so Agent Manager tab traffic cannot expire unsaved
|
||||
// config edits.
|
||||
const generation = ++this.settingsGeneration
|
||||
if (message.type === "requestAgentManagerSettings") {
|
||||
await this.sendAgentManagerSettings(message.projectId, false, generation, requestId)
|
||||
return true
|
||||
}
|
||||
if (message.type === "requestAgentManagerSettingsBranches" && message.projectId) {
|
||||
await this.sendAgentManagerSettingsBranches(message.projectId, generation, requestId)
|
||||
return true
|
||||
}
|
||||
if (message.type === "setAgentManagerDefaultBaseBranch" && message.projectId) {
|
||||
await handler.setDefaultBaseBranch(message.projectId, message.branch)
|
||||
if (this.settingsGeneration !== generation) return true
|
||||
await this.sendAgentManagerSettings(message.projectId, false, generation, requestId)
|
||||
return true
|
||||
}
|
||||
if (message.type === "configureAgentManagerSetupScript" && message.projectId) {
|
||||
await handler.configureSetupScript(message.projectId)
|
||||
if (this.settingsGeneration !== generation) return true
|
||||
await this.sendAgentManagerSettings(message.projectId, false, generation, requestId)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private async sendAgentManagerSettings(
|
||||
projectId: string | undefined,
|
||||
withBranches: boolean,
|
||||
generation: number,
|
||||
requestId: string,
|
||||
): Promise<void> {
|
||||
const handler = this.opts.agentManagerSettings
|
||||
if (!handler) return
|
||||
const projects = await handler.projects(projectId)
|
||||
if (this.settingsGeneration !== generation) return
|
||||
const selected = projects.some((project) => project.id === projectId) ? projectId : projects[0]?.id
|
||||
const branch = selected ? await handler.defaultBranch(selected) : undefined
|
||||
if (this.settingsGeneration !== generation) return
|
||||
const items = selected
|
||||
? projects.map((project) => (project.id === selected ? { ...project, defaultBranch: branch } : project))
|
||||
: projects
|
||||
this.postMessage({ type: "agentManagerSettingsLoaded", projects: items, projectId: selected, requestId })
|
||||
if (withBranches && selected) await this.sendAgentManagerSettingsBranches(selected, generation, requestId)
|
||||
}
|
||||
|
||||
private async sendAgentManagerSettingsBranches(
|
||||
projectId: string,
|
||||
generation: number,
|
||||
requestId: string,
|
||||
): Promise<void> {
|
||||
const handler = this.opts.agentManagerSettings
|
||||
if (!handler) return
|
||||
const data = await handler.branches(projectId)
|
||||
if (this.settingsGeneration !== generation) return
|
||||
this.postMessage(
|
||||
data
|
||||
? { type: "agentManagerSettingsBranchesLoaded", ...data, requestId }
|
||||
: {
|
||||
type: "agentManagerSettingsBranchesLoaded",
|
||||
projectId,
|
||||
branches: [],
|
||||
defaultBranch: "",
|
||||
requestId,
|
||||
error: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private bindingsFor(
|
||||
directory: string,
|
||||
targets:
|
||||
@@ -5182,6 +5272,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
// VS Code's native toolbar (restored in package.json) works everywhere.
|
||||
topBar: this.opts.hideTopBar !== true && isCursorHost(),
|
||||
topBarSurface: this.opts.topBarSurface === "tab" ? "tab_title" : "sidebar_title",
|
||||
agentManagerSettings: this.opts.agentManagerSettings !== undefined,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { KiloProvider } from "./KiloProvider"
|
||||
import { resolvePanelProjectDirectory } from "./project-directory"
|
||||
import type { KiloConnectionService } from "./services/cli-backend"
|
||||
import type { RemoteStatusService } from "./services/RemoteStatusService"
|
||||
import type { AgentManagerSettingsHandler } from "./kilo-provider/options"
|
||||
|
||||
type PanelView = "settings" | "profile" | "indexing"
|
||||
|
||||
@@ -27,15 +28,18 @@ export class SettingsEditorProvider implements vscode.Disposable {
|
||||
private panels = new Map<PanelView, vscode.WebviewPanel>()
|
||||
private providers = new Map<PanelView, KiloProvider>()
|
||||
private tabs = new Map<PanelView, string>()
|
||||
private projects = new Map<PanelView, string>()
|
||||
private remoteService: RemoteStatusService | null = null
|
||||
|
||||
constructor(
|
||||
private readonly extensionUri: vscode.Uri,
|
||||
private readonly connectionService: KiloConnectionService,
|
||||
private readonly context: vscode.ExtensionContext,
|
||||
private readonly agentManagerSettings?: AgentManagerSettingsHandler,
|
||||
) {}
|
||||
|
||||
private getProjectDirectory(): string | null {
|
||||
private getProjectDirectory(projectId?: string): string | null {
|
||||
if (projectId) return this.agentManagerSettings?.projectDirectory(projectId) ?? null
|
||||
const editor = vscode.window.activeTextEditor
|
||||
const active =
|
||||
editor?.document.uri.scheme === "file"
|
||||
@@ -53,19 +57,22 @@ export class SettingsEditorProvider implements vscode.Disposable {
|
||||
return view
|
||||
}
|
||||
|
||||
openPanel(view: PanelView, tab?: string): void {
|
||||
openPanel(view: PanelView, tab?: string, projectId?: string): void {
|
||||
if (tab) this.tabs.set(view, tab)
|
||||
if (projectId) this.projects.set(view, projectId)
|
||||
else this.projects.delete(view)
|
||||
|
||||
const projectDirectory = this.getProjectDirectory()
|
||||
const projectDirectory = this.getProjectDirectory(projectId)
|
||||
const existing = this.panels.get(view)
|
||||
if (existing) {
|
||||
this.providers.get(view)?.setProjectDirectory(projectDirectory)
|
||||
if (tab) {
|
||||
const provider = this.providers.get(view)
|
||||
provider?.postMessage({ type: "navigate", view, tab })
|
||||
}
|
||||
existing.reveal(vscode.ViewColumn.Active)
|
||||
this.providers.get(view)?.postMessage({ type: "navigate", view, ...(tab ? { tab } : {}) })
|
||||
this.providers.get(view)?.postMessage({
|
||||
type: "navigate",
|
||||
view,
|
||||
...(tab ? { tab } : {}),
|
||||
...(projectId ? { projectId } : {}),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -90,7 +97,7 @@ export class SettingsEditorProvider implements vscode.Disposable {
|
||||
panel.dispose()
|
||||
return
|
||||
}
|
||||
this.wirePanel(panel, view, this.getProjectDirectory())
|
||||
this.wirePanel(panel, view, this.getProjectDirectory(this.projects.get(view)))
|
||||
}
|
||||
|
||||
private wirePanel(panel: vscode.WebviewPanel, view: PanelView, projectDirectory: string | null): void {
|
||||
@@ -104,6 +111,7 @@ export class SettingsEditorProvider implements vscode.Disposable {
|
||||
const provider = new KiloProvider(this.extensionUri, this.connectionService, this.context, {
|
||||
projectDirectory,
|
||||
hideTopBar: true,
|
||||
agentManagerSettings: view === "settings" ? this.agentManagerSettings : undefined,
|
||||
})
|
||||
if (this.remoteService) {
|
||||
provider.setRemoteService(this.remoteService)
|
||||
@@ -123,7 +131,12 @@ export class SettingsEditorProvider implements vscode.Disposable {
|
||||
if (msg.type === "webviewReady") {
|
||||
// Small delay to let KiloProvider's own webviewReady handler finish first
|
||||
setTimeout(() => {
|
||||
provider.postMessage({ type: "navigate", view, tab: this.tabs.get(view) })
|
||||
provider.postMessage({
|
||||
type: "navigate",
|
||||
view,
|
||||
tab: this.tabs.get(view),
|
||||
projectId: this.projects.get(view),
|
||||
})
|
||||
}, 50)
|
||||
}
|
||||
})
|
||||
@@ -148,6 +161,7 @@ export class SettingsEditorProvider implements vscode.Disposable {
|
||||
this.panels.delete(view)
|
||||
this.providers.delete(view)
|
||||
this.tabs.delete(view)
|
||||
this.projects.delete(view)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -166,5 +180,6 @@ export class SettingsEditorProvider implements vscode.Disposable {
|
||||
this.panels.clear()
|
||||
this.providers.clear()
|
||||
this.tabs.clear()
|
||||
this.projects.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ import { ProjectContexts } from "./project/contexts"
|
||||
import { hydrateExpanded } from "./project/hydrate"
|
||||
import { createMultiVersion, type MultiVersionHost } from "./provider-multi-version"
|
||||
import { handleProjectMessage, type ProjectMessageDeps } from "./project/messages"
|
||||
import { createProjectWiring } from "./project/wiring"
|
||||
import { createProjectWiring, type ProjectWiring } from "./project/wiring"
|
||||
import { ProjectScope } from "./project/scope"
|
||||
import type { AgentManagerOutMessage, AgentManagerInMessage } from "./types"
|
||||
import type { Host, PanelContext, OutputHandle, Disposable } from "./host"
|
||||
@@ -116,10 +116,9 @@ export class AgentManagerProvider implements Disposable {
|
||||
private destination = new DestinationState()
|
||||
private closing: Promise<void> | undefined
|
||||
private onVisibilityChange: ((visible: boolean) => void) | undefined
|
||||
// Tracks sessions owned by this panel until they are explicitly closed.
|
||||
private panelSessions = new Set<string>()
|
||||
private busySessions = new Set<string>()
|
||||
|
||||
readonly settings: ProjectWiring["settings"]
|
||||
/** Session ID most recently loaded via `loadMessages`; updated synchronously. */
|
||||
private activeSessionId: string | undefined
|
||||
private visiblePresence = new AgentManagerVisiblePresence(
|
||||
@@ -195,12 +194,13 @@ export class AgentManagerProvider implements Disposable {
|
||||
expand: (ctx) => this.initExpanded(ctx),
|
||||
ready: (ctx) => initContextState(ctx, (...args) => this.log(...args)),
|
||||
push: () => this.pushProjects(),
|
||||
pushState: (ctx) => this.pushState(ctx),
|
||||
changed: () => this.onWorkspaceChanged(),
|
||||
refresh: () => this.pushState(),
|
||||
selected: (target) => this.postToWebview({ type: "agentManager.selectionActivated", target }),
|
||||
})
|
||||
this.registry = wiring.registry
|
||||
this.contexts = wiring.contexts
|
||||
this.settings = wiring.settings
|
||||
this.projects = wiring.messages
|
||||
this.unsubProjects = () => wiring.dispose()
|
||||
this.naming = new BranchNamingController({
|
||||
|
||||
@@ -82,6 +82,7 @@ function createMockHost(): Host {
|
||||
copyToClipboard: vi.fn(),
|
||||
capture: vi.fn(),
|
||||
openExternal: vi.fn(),
|
||||
openSettings: vi.fn(),
|
||||
refreshGit: vi.fn(),
|
||||
dispose: vi.fn(),
|
||||
}
|
||||
|
||||
@@ -178,6 +178,9 @@ export interface Host {
|
||||
/** Open a URL in the user's default browser. */
|
||||
openExternal(url: string): void
|
||||
|
||||
/** Open Kilo Settings, optionally focused on a tab and project. */
|
||||
openSettings(tab?: string, projectId?: string): void
|
||||
|
||||
/** Ask VS Code's git extension to re-scan repositories (e.g. after worktree ref migration). */
|
||||
refreshGit(): void
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ export interface ProjectMessageDeps {
|
||||
selected: (target: SidebarTarget) => void
|
||||
/** Show a user-facing error. */
|
||||
error: (message: string) => void
|
||||
/** Open the Kilo Settings editor, optionally on a tab and project. */
|
||||
openSettings: (tab?: string, projectId?: string) => void
|
||||
/** Ensure a context's repository state is ready (no-op once initialized). */
|
||||
ready: (ctx: ProjectContext) => Promise<ProjectInitResult>
|
||||
log: (...args: unknown[]) => void
|
||||
@@ -38,6 +40,10 @@ export interface ProjectMessageDeps {
|
||||
|
||||
/** Handle a project-management message. Returns true when the message was consumed. */
|
||||
export async function handleProjectMessage(m: AgentManagerInMessage, deps: ProjectMessageDeps): Promise<boolean> {
|
||||
if (m.type === "openSettingsPanel") {
|
||||
deps.openSettings(m.tab, m.projectId)
|
||||
return true
|
||||
}
|
||||
if (m.type === "agentManager.requestProjects") {
|
||||
deps.push()
|
||||
return true
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import type { BranchListItem } from "../git-import"
|
||||
import { normalizeBaseBranch } from "../base-branch"
|
||||
import { initContextState } from "./init"
|
||||
import type { ProjectContext } from "./context"
|
||||
import type { ProjectContexts, ProjectSnapshot } from "./contexts"
|
||||
|
||||
export interface SettingsProject {
|
||||
id: string
|
||||
root: string
|
||||
label: string
|
||||
pinned: boolean
|
||||
missing: boolean
|
||||
defaultBaseBranch?: string
|
||||
defaultBranch?: string
|
||||
setupScriptPath?: string
|
||||
}
|
||||
|
||||
export interface SettingsBranches {
|
||||
projectId: string
|
||||
branches: BranchListItem[]
|
||||
defaultBranch: string
|
||||
configuredBaseBranch?: string
|
||||
setupScriptPath?: string
|
||||
}
|
||||
|
||||
export interface SettingsHandler {
|
||||
projects(projectId?: string): Promise<SettingsProject[]>
|
||||
projectDirectory(projectId: string): string | undefined
|
||||
defaultBranch(projectId: string): Promise<string | undefined>
|
||||
branches(projectId: string): Promise<SettingsBranches | undefined>
|
||||
setDefaultBaseBranch(projectId: string, branch?: string): Promise<void>
|
||||
configureSetupScript(projectId: string): Promise<void>
|
||||
}
|
||||
|
||||
export function createSettingsHandler(opts: {
|
||||
contexts: ProjectContexts
|
||||
open: (path: string) => Promise<void>
|
||||
push: (ctx: ProjectContext) => void
|
||||
log: (...args: unknown[]) => void
|
||||
}): SettingsHandler {
|
||||
return {
|
||||
projects: (projectId) => loadSettingsProjects(opts.contexts, projectId, opts.log),
|
||||
projectDirectory: (id) => settingsDirectory(opts.contexts, id),
|
||||
defaultBranch: (id) => settingsDefaultBranch(opts.contexts, id, opts.log),
|
||||
branches: (id) => settingsBranches(opts.contexts, id, opts.log, opts.push),
|
||||
setDefaultBaseBranch: (id, branch) => setSettingsDefaultBaseBranch(opts.contexts, id, branch, opts.log, opts.push),
|
||||
configureSetupScript: (id) => configureSettingsSetupScript(opts.contexts, id, opts.open, opts.log),
|
||||
}
|
||||
}
|
||||
|
||||
export function settingsProjects(contexts: ProjectContexts): SettingsProject[] {
|
||||
return contexts.snapshots().map((project) => settingsProject(contexts, project))
|
||||
}
|
||||
|
||||
async function loadSettingsProjects(
|
||||
contexts: ProjectContexts,
|
||||
projectId: string | undefined,
|
||||
log: (...args: unknown[]) => void,
|
||||
): Promise<SettingsProject[]> {
|
||||
const snapshots = contexts.snapshots()
|
||||
const selected = snapshots.some((project) => project.id === projectId) ? projectId : snapshots.at(0)?.id
|
||||
const ctx = selected ? contexts.resolve(selected) : undefined
|
||||
if (ctx && !ctx.missing()) {
|
||||
const init = await initContextState(ctx, log)
|
||||
if (init.ok && init.current) ctx.setupService()
|
||||
}
|
||||
return settingsProjects(contexts)
|
||||
}
|
||||
|
||||
function settingsProject(contexts: ProjectContexts, project: ProjectSnapshot): SettingsProject {
|
||||
const ctx = contexts.get(project.id)
|
||||
return {
|
||||
id: project.id,
|
||||
root: project.root,
|
||||
label: project.label,
|
||||
pinned: project.pinned,
|
||||
missing: project.missing,
|
||||
defaultBaseBranch: ctx?.peekState()?.getDefaultBaseBranch(),
|
||||
setupScriptPath: ctx?.peekSetup()?.resolveScript()?.path,
|
||||
}
|
||||
}
|
||||
|
||||
export function settingsDirectory(contexts: ProjectContexts, projectId: string): string | undefined {
|
||||
return contexts.resolve(projectId)?.root
|
||||
}
|
||||
|
||||
export async function settingsDefaultBranch(
|
||||
contexts: ProjectContexts,
|
||||
projectId: string,
|
||||
log: (...args: unknown[]) => void,
|
||||
): Promise<string | undefined> {
|
||||
const ctx = contexts.resolve(projectId)
|
||||
if (!ctx || ctx.missing()) return undefined
|
||||
return ctx
|
||||
.worktreeManager()
|
||||
.defaultBranch()
|
||||
.catch((err) => {
|
||||
log(`Failed to load settings default branch for ${projectId}:`, err)
|
||||
return undefined
|
||||
})
|
||||
}
|
||||
|
||||
export async function settingsBranches(
|
||||
contexts: ProjectContexts,
|
||||
projectId: string,
|
||||
log: (...args: unknown[]) => void,
|
||||
push: (ctx: ProjectContext) => void,
|
||||
): Promise<SettingsBranches | undefined> {
|
||||
const ctx = contexts.resolve(projectId)
|
||||
if (!ctx || ctx.missing()) return undefined
|
||||
const init = await initContextState(ctx, log)
|
||||
if (!init.ok || !init.current) return undefined
|
||||
const result = await ctx
|
||||
.worktreeManager()
|
||||
.listBranches()
|
||||
.catch((err) => {
|
||||
log(`Failed to load settings branches for ${projectId}:`, err)
|
||||
return undefined
|
||||
})
|
||||
if (!result) return undefined
|
||||
const configured = ctx.peekState()?.getDefaultBaseBranch()
|
||||
const valid = configured && result.branches.some((branch) => branch.name === configured)
|
||||
if (configured && !valid) {
|
||||
ctx.peekState()?.setDefaultBaseBranch(undefined)
|
||||
push(ctx)
|
||||
}
|
||||
return {
|
||||
projectId,
|
||||
branches: result.branches,
|
||||
defaultBranch: result.defaultBranch,
|
||||
configuredBaseBranch: valid ? configured : undefined,
|
||||
setupScriptPath: ctx.setupService().resolveScript()?.path,
|
||||
}
|
||||
}
|
||||
|
||||
export async function setSettingsDefaultBaseBranch(
|
||||
contexts: ProjectContexts,
|
||||
projectId: string,
|
||||
branch: string | undefined,
|
||||
log: (...args: unknown[]) => void,
|
||||
push: (ctx: ProjectContext) => void,
|
||||
): Promise<void> {
|
||||
const ctx = contexts.resolve(projectId)
|
||||
if (!ctx || ctx.missing()) return
|
||||
const init = await initContextState(ctx, log)
|
||||
if (!init.ok || !init.current) return
|
||||
ctx.peekState()?.setDefaultBaseBranch(normalizeBaseBranch(branch))
|
||||
push(ctx)
|
||||
}
|
||||
|
||||
export async function configureSettingsSetupScript(
|
||||
contexts: ProjectContexts,
|
||||
projectId: string,
|
||||
open: (path: string) => Promise<void>,
|
||||
log: (...args: unknown[]) => void,
|
||||
): Promise<void> {
|
||||
const ctx = contexts.resolve(projectId)
|
||||
if (!ctx || ctx.missing()) return
|
||||
const service = ctx.setupService()
|
||||
try {
|
||||
if (!service.hasScript()) await service.createDefaultScript()
|
||||
const script = service.resolveScript()
|
||||
if (!script) return
|
||||
await open(script.path)
|
||||
} catch (err) {
|
||||
log(`Failed to open setup script for ${projectId}:`, err)
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,13 @@ import { ProjectRegistry } from "./registry"
|
||||
import type { ProjectContext, ProjectInitResult } from "./context"
|
||||
import { ProjectContexts, type ProjectSnapshot } from "./contexts"
|
||||
import type { ProjectMessageDeps } from "./messages"
|
||||
import { createSettingsHandler, type SettingsHandler } from "./settings"
|
||||
|
||||
export interface ProjectWiring {
|
||||
registry: ProjectRegistry
|
||||
contexts: ProjectContexts
|
||||
/** Project-scoped settings handler shared with the Kilo Settings editor. */
|
||||
settings: SettingsHandler
|
||||
messages: ProjectMessageDeps
|
||||
/** Payload for the agentManager.projects webview message. */
|
||||
snapshots(): { type: "agentManager.projects"; multiProject: boolean; projects: ProjectSnapshot[] }
|
||||
@@ -35,10 +38,10 @@ export function createProjectWiring(opts: {
|
||||
ready: (ctx: ProjectContext) => Promise<ProjectInitResult>
|
||||
/** Push the project catalog to the webview. */
|
||||
push: () => void
|
||||
/** Push one project's state (or every context when omitted) to the webview. */
|
||||
pushState: (ctx?: ProjectContext) => void
|
||||
/** Re-derive the pinned project after workspace folder changes. */
|
||||
changed: () => void
|
||||
/** Re-push worktree state (e.g. after the flag toggles). */
|
||||
refresh: () => void
|
||||
/** Acknowledge an atomically validated sidebar selection. */
|
||||
selected: (target: import("./route").SidebarTarget) => void
|
||||
}): ProjectWiring {
|
||||
@@ -64,8 +67,15 @@ export function createProjectWiring(opts: {
|
||||
push: opts.push,
|
||||
selected: opts.selected,
|
||||
error: (message) => opts.host.showError(message),
|
||||
openSettings: (tab, projectId) => opts.host.openSettings(tab, projectId),
|
||||
log: opts.log,
|
||||
}
|
||||
const settings = createSettingsHandler({
|
||||
contexts,
|
||||
open: (path) => opts.host.openDocument(path),
|
||||
push: opts.pushState,
|
||||
log: opts.log,
|
||||
})
|
||||
const listeners: Disposable[] = [
|
||||
opts.host.onDidChangeWorkspaceFolders(() => opts.changed()),
|
||||
opts.host.onDidChangeMultiProject((enabled) => {
|
||||
@@ -74,12 +84,13 @@ export function createProjectWiring(opts: {
|
||||
if (pinned) opts.activate(pinned)
|
||||
}
|
||||
opts.push()
|
||||
opts.refresh()
|
||||
opts.pushState()
|
||||
}),
|
||||
]
|
||||
return {
|
||||
registry,
|
||||
contexts,
|
||||
settings,
|
||||
messages,
|
||||
snapshots: () => ({
|
||||
type: "agentManager.projects",
|
||||
|
||||
@@ -673,6 +673,12 @@ interface RenameWorktreeIn {
|
||||
label: string
|
||||
}
|
||||
|
||||
interface OpenSettingsPanelIn {
|
||||
type: "openSettingsPanel"
|
||||
tab?: string
|
||||
projectId?: string
|
||||
}
|
||||
|
||||
interface RequestStateIn {
|
||||
type: "agentManager.requestState"
|
||||
}
|
||||
@@ -1093,6 +1099,7 @@ export type AgentManagerInMessage =
|
||||
| RequestRepoInfoIn
|
||||
| CreateMultiVersionIn
|
||||
| RenameWorktreeIn
|
||||
| OpenSettingsPanelIn
|
||||
| RequestStateIn
|
||||
| RequestBranchesIn
|
||||
| SetTabOrderIn
|
||||
|
||||
@@ -320,6 +320,10 @@ export class VscodeHost implements Host {
|
||||
void vscode.env.openExternal(vscode.Uri.parse(url))
|
||||
}
|
||||
|
||||
openSettings(tab?: string, projectId?: string): void {
|
||||
void vscode.commands.executeCommand("kilo-code.new.settingsButtonClicked", tab, projectId)
|
||||
}
|
||||
|
||||
refreshGit(): void {
|
||||
void vscode.commands.executeCommand("git.refresh")
|
||||
}
|
||||
|
||||
@@ -295,7 +295,9 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
context.subscriptions.push(diffVirtualProvider)
|
||||
|
||||
// Create standalone editor providers (open in editor area, not sidebar)
|
||||
const settingsEditorProvider = new SettingsEditorProvider(context.extensionUri, connectionService, context)
|
||||
const settingsEditorProvider = new SettingsEditorProvider(context.extensionUri, connectionService, context, {
|
||||
...agentManagerProvider.settings,
|
||||
})
|
||||
settingsEditorProvider.setRemoteService(remoteService)
|
||||
const marketplacePanelProvider = new MarketplacePanelProvider(context.extensionUri, connectionService, context)
|
||||
context.subscriptions.push(settingsEditorProvider, marketplacePanelProvider)
|
||||
@@ -429,8 +431,8 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand("kilo-code.new.profileButtonClicked", () => {
|
||||
settingsEditorProvider.openPanel("profile")
|
||||
}),
|
||||
vscode.commands.registerCommand("kilo-code.new.settingsButtonClicked", (tab?: string) => {
|
||||
settingsEditorProvider.openPanel("settings", tab)
|
||||
vscode.commands.registerCommand("kilo-code.new.settingsButtonClicked", (tab?: string, projectId?: string) => {
|
||||
settingsEditorProvider.openPanel("settings", tab, projectId)
|
||||
}),
|
||||
vscode.commands.registerCommand("kilo-code.new.openIndexingSettings", () => {
|
||||
settingsEditorProvider.openPanel("settings", "indexing")
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { ProjectRouteService } from "../agent-manager/project/route"
|
||||
import type { SettingsHandler } from "../agent-manager/project/settings"
|
||||
|
||||
export type AgentManagerSettingsHandler = SettingsHandler
|
||||
|
||||
export type KiloProviderOptions = {
|
||||
/** Context key updated from focus events reported by this provider's webview. */
|
||||
@@ -47,4 +50,6 @@ export type KiloProviderOptions = {
|
||||
hideTopBar?: boolean
|
||||
/** Reports "Open in Tab" as the top bar's telemetry surface instead of the sidebar default. */
|
||||
topBarSurface?: "tab"
|
||||
/** Project-aware settings used by the standalone Agent Manager settings tab. */
|
||||
agentManagerSettings?: AgentManagerSettingsHandler
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ export function buildWebviewHtml(
|
||||
/** Sidebar top bar visibility and telemetry surface for the shared webview bundle (App.tsx). Unused by the Agent Manager bundle. */
|
||||
topBar?: boolean
|
||||
topBarSurface?: string
|
||||
agentManagerSettings?: boolean
|
||||
},
|
||||
): string {
|
||||
const nonce = getNonce()
|
||||
@@ -107,7 +108,7 @@ export function buildWebviewHtml(
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script nonce="${nonce}">window.ICONS_BASE_URI = "${opts.iconsBaseUri}"; window.KILO_SHIKI_WORKER_URI = "${opts.workerUri}"; window.KILO_MARKDOWN_SHIKI_WORKER_URI = "${markdownWorkerUri}"; window.KILO_TOP_BAR = ${opts.topBar !== false}; window.KILO_TOP_BAR_SURFACE = "${opts.topBarSurface ?? "sidebar_title"}";</script>
|
||||
<script nonce="${nonce}">window.ICONS_BASE_URI = "${opts.iconsBaseUri}"; window.KILO_SHIKI_WORKER_URI = "${opts.workerUri}"; window.KILO_MARKDOWN_SHIKI_WORKER_URI = "${markdownWorkerUri}"; window.KILO_TOP_BAR = ${opts.topBar !== false}; window.KILO_TOP_BAR_SURFACE = "${opts.topBarSurface ?? "sidebar_title"}"; window.KILO_AGENT_MANAGER_SETTINGS = ${opts.agentManagerSettings === true};</script>
|
||||
<script nonce="${nonce}" src="${opts.scriptUri}"></script>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
@@ -59,7 +59,6 @@ const TSX_FILES = [
|
||||
path.join(ROOT, "webview-ui/agent-manager/ClosableTab.tsx"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/InspectorTabStrip.tsx"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/ProjectBranchDialog.tsx"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/DefaultBaseBranchDialog.tsx"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/tab-rendering.tsx"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/terminal/TerminalTab.tsx"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/terminal/SideTerminalPanel.tsx"),
|
||||
@@ -904,35 +903,6 @@ describe("KiloProvider — pending session refresh on reconnect", () => {
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// handleChangeDefaultBaseBranch — listener leak fix
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("Agent Manager — dialog listener cleanup", () => {
|
||||
const tsx = fs.readFileSync(path.join(ROOT, "webview-ui/agent-manager/DefaultBaseBranchDialog.tsx"), "utf-8")
|
||||
|
||||
/**
|
||||
* Regression: handleChangeDefaultBaseBranch subscribes to vscode.onMessage
|
||||
* for branch data. Previously unsub() was only called inside selectBranch()
|
||||
* and the Escape keydown handler. If the dialog closed via backdrop click or
|
||||
* external dialog.close(), the listener leaked and stacked on every reopen.
|
||||
*
|
||||
* The fix ties unsub() to the dialog component's Solid cleanup so it always
|
||||
* disposes regardless of how the dialog closes.
|
||||
*/
|
||||
it("DefaultBaseBranchDialog disposes its message listener on cleanup", () => {
|
||||
expect(tsx).toContain("const unsub = vscode.onMessage")
|
||||
expect(tsx).toContain("onCleanup(unsub)")
|
||||
})
|
||||
|
||||
it("select does not manually call unsub (handled by onCleanup)", () => {
|
||||
const selStart = tsx.indexOf("const select =")
|
||||
expect(selStart, "select must exist").toBeGreaterThan(-1)
|
||||
const selEnd = tsx.indexOf("}", selStart + 40)
|
||||
const selBody = tsx.slice(selStart, selEnd + 1)
|
||||
expect(selBody, "select should not call unsub() directly").not.toContain("unsub()")
|
||||
})
|
||||
})
|
||||
|
||||
describe("SetupScriptRunner — task execution model", () => {
|
||||
const runner = fs.readFileSync(SETUP_SCRIPT_RUNNER_FILE, "utf-8")
|
||||
const taskAdapter = fs.readFileSync(path.join(ROOT, "src/agent-manager/task-runner.ts"), "utf-8")
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it } from "bun:test"
|
||||
import { readFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
|
||||
const ROOT = join(import.meta.dir, "..", "..")
|
||||
|
||||
describe("Agent Manager settings navigation", () => {
|
||||
const actions = readFileSync(join(ROOT, "webview-ui", "agent-manager", "ProjectActions.tsx"), "utf8")
|
||||
const project = readFileSync(join(ROOT, "webview-ui", "agent-manager", "ProjectSidebarBody.tsx"), "utf8")
|
||||
const settings = readFileSync(join(ROOT, "webview-ui", "src", "components", "settings", "Settings.tsx"), "utf8")
|
||||
const branchDialog = readFileSync(join(ROOT, "webview-ui", "agent-manager", "ProjectBranchDialog.tsx"), "utf8")
|
||||
|
||||
it("opens the project settings tab with the owning project id", () => {
|
||||
expect(actions).toContain("onClick={props.onSettings}")
|
||||
expect(project).toContain('tab: "agentManager"')
|
||||
expect(project).toContain("projectId: props.project.id")
|
||||
})
|
||||
|
||||
it("renders a project selector and project-scoped settings controls", () => {
|
||||
expect(settings).toContain('type: "requestAgentManagerSettings"')
|
||||
expect(settings).toContain('type: "requestAgentManagerSettingsBranches"')
|
||||
expect(settings).toContain('type: "setAgentManagerDefaultBaseBranch"')
|
||||
expect(settings).toContain('type: "configureAgentManagerSetupScript"')
|
||||
})
|
||||
|
||||
it("keeps the repository default branch selectable without an empty value", () => {
|
||||
expect(settings).toContain("<ProjectBranchDialog")
|
||||
expect(branchDialog).toContain('label: language.t("agentManager.worktree.defaultBaseBranchAuto")')
|
||||
expect(branchDialog).toContain("onSelect: () => select()")
|
||||
})
|
||||
|
||||
it("places the Agent Manager settings tab under Auto-Approve", () => {
|
||||
const autoApprove = settings.indexOf('value="autoApprove"')
|
||||
const agentManager = settings.indexOf('value="agentManager"')
|
||||
const browser = settings.indexOf('value="browser"')
|
||||
expect(autoApprove).toBeGreaterThan(-1)
|
||||
expect(autoApprove).toBeLessThan(agentManager)
|
||||
expect(agentManager).toBeLessThan(browser)
|
||||
})
|
||||
|
||||
it("loads branches only when the default branch picker opens", () => {
|
||||
expect(settings).toContain("loadBranches(id)")
|
||||
expect(settings).toContain("dialog.show(() =>")
|
||||
})
|
||||
|
||||
it("opens the existing branch picker dialog", () => {
|
||||
expect(settings).toContain("dialog.show(() =>")
|
||||
expect(settings).toContain("<ProjectBranchDialog")
|
||||
})
|
||||
})
|
||||
@@ -78,7 +78,6 @@ import { ProviderShell } from "../src/context/provider-shell"
|
||||
import { ChatView } from "../src/components/chat"
|
||||
import HistoryView from "../src/components/history/HistoryView"
|
||||
import { NewWorktreeDialog } from "./NewWorktreeDialog"
|
||||
import { DefaultBaseBranchDialog } from "./DefaultBaseBranchDialog"
|
||||
import { createModeRouter } from "./mode-router"
|
||||
import { ProjectList } from "./ProjectList"
|
||||
import { SidebarBody } from "./SidebarBody"
|
||||
@@ -258,9 +257,6 @@ const AgentManagerContent: Component = () => {
|
||||
const [sessionsLoaded, setSessionsLoaded] = createSignal(false)
|
||||
const [isGitRepo, setIsGitRepo] = createSignal(true)
|
||||
const [repoDetectedBranch, setRepoDetectedBranch] = createSignal<string | undefined>()
|
||||
const defaultBaseBranch = () => registry.active().defaultBaseBranch()
|
||||
const setDefaultBaseBranch = (v: Parameters<Setter<string | undefined>>[0]) =>
|
||||
registry.active().setDefaultBaseBranch(v)
|
||||
const [projectList, setProjectList] = createSignal<AgentProjectSnapshot[]>([])
|
||||
const [multiProject, setMultiProject] = createSignal(false)
|
||||
const [currentProjectId, setCurrentProjectId] = createSignal<string | undefined>()
|
||||
@@ -274,7 +270,9 @@ const AgentManagerContent: Component = () => {
|
||||
)
|
||||
const isActivePayload = (pid: string | undefined) =>
|
||||
projectList().length === 0 || pid === undefined || pid === activeProjectId()
|
||||
const repoDefaultBranch = () => defaultBaseBranch() ?? repoDetectedBranch() ?? "main"
|
||||
|
||||
const repoDefaultBranch = () => repoDetectedBranch() ?? "main"
|
||||
|
||||
const DEFAULT_SIDEBAR_WIDTH = 260
|
||||
const MIN_SIDEBAR_WIDTH = 200
|
||||
const MAX_SIDEBAR_WIDTH_RATIO = 0.4
|
||||
@@ -1757,23 +1755,6 @@ const AgentManagerContent: Component = () => {
|
||||
|
||||
const revertCtl = createRevertFile(diffScopeId, diffCtx, () => review.scope(), vscode, showToast, t, activeProjectId)
|
||||
|
||||
const handleConfigureSetupScript = () => {
|
||||
vscode.postMessage({ type: "agentManager.configureSetupScript" })
|
||||
}
|
||||
const setupScript = metrics.click("configure_setup_script", "worktree_settings", handleConfigureSetupScript)
|
||||
|
||||
const handleChangeDefaultBaseBranch = () => {
|
||||
dialog.show(() => (
|
||||
<DefaultBaseBranchDialog
|
||||
selected={defaultBaseBranch()}
|
||||
detected={repoDetectedBranch()}
|
||||
onSelect={setDefaultBaseBranch}
|
||||
onDetected={setRepoDetectedBranch}
|
||||
onClose={() => dialog.close()}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
const handleShowKeyboardShortcuts = () => {
|
||||
const categories = buildShortcutCategories(kb(), t)
|
||||
dialog.show(() => (
|
||||
@@ -2364,8 +2345,7 @@ const AgentManagerContent: Component = () => {
|
||||
onNewWorktree={showNewWorktreeDialog}
|
||||
onNewSection={newSection}
|
||||
onShortcuts={metrics.click("keyboard_shortcuts", "worktrees_header", handleShowKeyboardShortcuts)}
|
||||
onSetup={setupScript}
|
||||
onBranch={handleChangeDefaultBaseBranch}
|
||||
projectId={activeProjectId()}
|
||||
sections={sections}
|
||||
sortedWorktrees={sortedWorktrees}
|
||||
worktrees={worktrees}
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
/** @jsxImportSource solid-js */
|
||||
|
||||
import { createMemo, createSignal, onCleanup, type Component } from "solid-js"
|
||||
import { Dialog } from "@kilocode/kilo-ui/dialog"
|
||||
import { BranchSelect } from "../src/components/shared/BranchSelect"
|
||||
import { useLanguage } from "../src/context/language"
|
||||
import { useVSCode } from "../src/context/vscode"
|
||||
import type { AgentManagerBranchesMessage, BranchInfo } from "../src/types/messages"
|
||||
|
||||
interface Props {
|
||||
selected?: string
|
||||
detected?: string
|
||||
onSelect: (branch?: string) => void
|
||||
onDetected: (branch: string) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export const DefaultBaseBranchDialog: Component<Props> = (props) => {
|
||||
const { t } = useLanguage()
|
||||
const vscode = useVSCode()
|
||||
const [search, setSearch] = createSignal("")
|
||||
const [branches, setBranches] = createSignal<BranchInfo[]>([])
|
||||
const [loading, setLoading] = createSignal(true)
|
||||
const [highlighted, setHighlighted] = createSignal(-1)
|
||||
const filtered = createMemo(() => {
|
||||
const value = search().toLowerCase()
|
||||
return value ? branches().filter((branch) => branch.name.toLowerCase().includes(value)) : branches()
|
||||
})
|
||||
const select = (branch?: string) => {
|
||||
vscode.postMessage({ type: "agentManager.setDefaultBaseBranch", branch })
|
||||
props.onSelect(branch)
|
||||
props.onClose()
|
||||
}
|
||||
const unsub = vscode.onMessage((message) => {
|
||||
if (message.type !== "agentManager.branches") return
|
||||
const event = message as AgentManagerBranchesMessage
|
||||
setBranches(event.branches)
|
||||
if (event.defaultBranch) props.onDetected(event.defaultBranch)
|
||||
setLoading(false)
|
||||
})
|
||||
onCleanup(unsub)
|
||||
vscode.postMessage({ type: "agentManager.requestBranches" })
|
||||
|
||||
const keydown = (event: KeyboardEvent) => {
|
||||
const items = filtered()
|
||||
const total = items.length + 1
|
||||
if (event.key === "ArrowDown") {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setHighlighted((value) => Math.min(value + 1, total - 2))
|
||||
return
|
||||
}
|
||||
if (event.key === "ArrowUp") {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setHighlighted((value) => Math.max(value - 1, -1))
|
||||
return
|
||||
}
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
const index = highlighted()
|
||||
if (index === -1) {
|
||||
select()
|
||||
return
|
||||
}
|
||||
const branch = items[index]
|
||||
if (branch) select(branch.name)
|
||||
return
|
||||
}
|
||||
if (event.key !== "Escape") return
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
props.onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog title={t("agentManager.worktree.defaultBaseBranch")} fit>
|
||||
<div class="am-default-base-branch">
|
||||
<BranchSelect
|
||||
branches={filtered()}
|
||||
loading={loading()}
|
||||
search={search()}
|
||||
onSearch={(value) => {
|
||||
setSearch(value)
|
||||
setHighlighted(-1)
|
||||
}}
|
||||
onSelect={(branch) => select(branch.name)}
|
||||
onSearchKeyDown={keydown}
|
||||
selected={props.selected}
|
||||
highlighted={highlighted()}
|
||||
onHighlight={setHighlighted}
|
||||
searchPlaceholder={t("agentManager.dialog.searchBranches")}
|
||||
emptyLabel={t("agentManager.import.noMatchingBranches")}
|
||||
loadingLabel={t("agentManager.import.loadingBranches")}
|
||||
defaultLabel={t("agentManager.dialog.branchBadge.default")}
|
||||
remoteLabel={t("agentManager.dialog.branchBadge.remote")}
|
||||
defaultName={props.selected}
|
||||
autoOption={{
|
||||
label: t("agentManager.worktree.defaultBaseBranchAuto"),
|
||||
hint: props.detected,
|
||||
active: !props.selected,
|
||||
highlighted: highlighted() === -1,
|
||||
onSelect: () => select(),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -16,8 +16,7 @@ interface Props {
|
||||
onCreate: () => void
|
||||
onNew: () => void
|
||||
onSection: () => void
|
||||
onSetup: () => void
|
||||
onBranch: () => void
|
||||
onSettings: () => void
|
||||
}
|
||||
|
||||
export const ProjectActions: Component<Props> = (props) => (
|
||||
@@ -70,27 +69,12 @@ export const ProjectActions: Component<Props> = (props) => (
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<DropdownMenu gutter={4} placement="bottom-end">
|
||||
<DropdownMenu.Trigger
|
||||
as={IconButton}
|
||||
icon="settings-gear"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
label={props.t("agentManager.worktree.settings")}
|
||||
/>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content class="am-split-menu">
|
||||
<DropdownMenu.Item onSelect={props.onSetup}>
|
||||
<DropdownMenu.ItemLabel>{props.t("agentManager.worktree.setupScript")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onSelect={props.onBranch}>
|
||||
<DropdownMenu.ItemLabel>
|
||||
{props.t("agentManager.worktree.defaultBaseBranch")}: {props.branch}
|
||||
</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu>
|
||||
<IconButton
|
||||
icon="settings-gear"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
label={props.t("agentManager.worktree.settings")}
|
||||
onClick={props.onSettings}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,67 +1,92 @@
|
||||
/** @jsxImportSource solid-js */
|
||||
|
||||
import { createMemo, createSignal, onCleanup, onMount, type Component } from "solid-js"
|
||||
import { createMemo, createSignal, type Component } from "solid-js"
|
||||
import { Dialog } from "@kilocode/kilo-ui/dialog"
|
||||
import "./agent-manager.css"
|
||||
import "./agent-manager-review.css"
|
||||
import { BranchSelect } from "../src/components/shared/BranchSelect"
|
||||
import { useLanguage } from "../src/context/language"
|
||||
import { useVSCode } from "../src/context/vscode"
|
||||
import type { AgentManagerBranchesMessage, BranchInfo } from "../src/types/messages"
|
||||
import type { BranchInfo } from "../src/types/messages"
|
||||
|
||||
interface Props {
|
||||
projectId: string
|
||||
selected?: string
|
||||
detected?: string
|
||||
branches: () => BranchInfo[]
|
||||
loading: () => boolean
|
||||
onSelect: (branch?: string) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export const ProjectBranchDialog: Component<Props> = (props) => {
|
||||
const { t } = useLanguage()
|
||||
const vscode = useVSCode()
|
||||
const language = useLanguage()
|
||||
const [search, setSearch] = createSignal("")
|
||||
const [branches, setBranches] = createSignal<BranchInfo[]>([])
|
||||
const [loading, setLoading] = createSignal(true)
|
||||
const [highlighted, setHighlighted] = createSignal(-1)
|
||||
const filtered = createMemo(() => {
|
||||
const value = search().toLowerCase()
|
||||
return value ? branches().filter((branch) => branch.name.toLowerCase().includes(value)) : branches()
|
||||
const items = props.branches()
|
||||
return value ? items.filter((branch) => branch.name.toLowerCase().includes(value)) : items
|
||||
})
|
||||
const select = (branch?: string) => {
|
||||
vscode.postMessage({ type: "agentManager.setDefaultBaseBranch", projectId: props.projectId, branch })
|
||||
props.onSelect(branch)
|
||||
props.onClose()
|
||||
}
|
||||
const keydown = (event: KeyboardEvent) => {
|
||||
const items = filtered()
|
||||
const total = items.length + 1
|
||||
if (event.key === "ArrowDown") {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setHighlighted((value) => Math.min(value + 1, total - 2))
|
||||
return
|
||||
}
|
||||
if (event.key === "ArrowUp") {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setHighlighted((value) => Math.max(value - 1, -1))
|
||||
return
|
||||
}
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
const index = highlighted()
|
||||
if (index === -1) {
|
||||
select()
|
||||
return
|
||||
}
|
||||
const branch = items[index]
|
||||
if (branch) select(branch.name)
|
||||
return
|
||||
}
|
||||
if (event.key !== "Escape") return
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
props.onClose()
|
||||
}
|
||||
const unsubscribe = vscode.onMessage((message) => {
|
||||
if (message.type !== "agentManager.branches") return
|
||||
const event = message as AgentManagerBranchesMessage
|
||||
if (event.projectId !== props.projectId) return
|
||||
setBranches(event.branches)
|
||||
setLoading(false)
|
||||
})
|
||||
onCleanup(unsubscribe)
|
||||
onMount(() => vscode.postMessage({ type: "agentManager.requestBranches", projectId: props.projectId }))
|
||||
|
||||
return (
|
||||
<Dialog title={t("agentManager.worktree.defaultBaseBranch")} fit>
|
||||
<Dialog title={language.t("agentManager.worktree.defaultBaseBranch")} fit>
|
||||
<div class="am-default-base-branch">
|
||||
<BranchSelect
|
||||
branches={filtered()}
|
||||
loading={loading()}
|
||||
loading={props.loading()}
|
||||
search={search()}
|
||||
onSearch={(value) => {
|
||||
setSearch(value)
|
||||
setHighlighted(-1)
|
||||
}}
|
||||
onSelect={(branch) => select(branch.name)}
|
||||
onSearchKeyDown={keydown}
|
||||
selected={props.selected}
|
||||
highlighted={highlighted()}
|
||||
onHighlight={setHighlighted}
|
||||
searchPlaceholder={t("agentManager.dialog.searchBranches")}
|
||||
emptyLabel={t("agentManager.import.noMatchingBranches")}
|
||||
loadingLabel={t("agentManager.import.loadingBranches")}
|
||||
defaultLabel={t("agentManager.dialog.branchBadge.default")}
|
||||
remoteLabel={t("agentManager.dialog.branchBadge.remote")}
|
||||
searchPlaceholder={language.t("agentManager.dialog.searchBranches")}
|
||||
emptyLabel={language.t("agentManager.import.noMatchingBranches")}
|
||||
loadingLabel={language.t("agentManager.import.loadingBranches")}
|
||||
defaultLabel={language.t("agentManager.dialog.branchBadge.default")}
|
||||
remoteLabel={language.t("agentManager.dialog.branchBadge.remote")}
|
||||
defaultName={props.selected ?? props.detected}
|
||||
autoOption={{
|
||||
label: t("agentManager.worktree.defaultBaseBranchAuto"),
|
||||
label: language.t("agentManager.worktree.defaultBaseBranchAuto"),
|
||||
hint: props.detected,
|
||||
active: !props.selected,
|
||||
highlighted: highlighted() === -1,
|
||||
|
||||
@@ -18,7 +18,6 @@ import { SidebarSearchMenu, type SidebarSearchMenuRef } from "./SidebarSearchMen
|
||||
import type { SidebarSearchItem } from "./sidebar-search"
|
||||
import { LOCAL } from "./navigate"
|
||||
import { NewWorktreeDialog } from "./NewWorktreeDialog"
|
||||
import { ProjectBranchDialog } from "./ProjectBranchDialog"
|
||||
import type { ProjectStore } from "./project/store"
|
||||
import type { ModeRouter } from "./mode-router"
|
||||
|
||||
@@ -155,15 +154,6 @@ export const ProjectList: Component<Props> = (props) => {
|
||||
/>
|
||||
))
|
||||
}
|
||||
const defaultBranch = (projectId: string, selected?: string, detected?: string) =>
|
||||
dialog.show(() => (
|
||||
<ProjectBranchDialog
|
||||
projectId={projectId}
|
||||
selected={selected}
|
||||
detected={detected}
|
||||
onClose={() => dialog.close()}
|
||||
/>
|
||||
))
|
||||
return (
|
||||
<ProjectsSection
|
||||
projects={props.projects}
|
||||
@@ -240,7 +230,6 @@ export const ProjectList: Component<Props> = (props) => {
|
||||
onSelectWorktree={(projectId, worktreeId) => select({ projectId, kind: "worktree", worktreeId })}
|
||||
onSelectSession={(projectId, sessionId) => select({ projectId, kind: "session", sessionId })}
|
||||
onNewWorktree={newWorktree}
|
||||
onDefaultBranch={defaultBranch}
|
||||
shortcutMap={props.shortcutMap}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -57,7 +57,6 @@ interface Props {
|
||||
onSelectWorktree: (projectId: string, worktreeId: string) => void
|
||||
onSelectSession: (projectId: string, sessionId: string) => void
|
||||
onNewWorktree: (projectId: string) => void
|
||||
onDefaultBranch: (projectId: string, selected?: string, detected?: string) => void
|
||||
shortcutMap?: () => Map<string, number>
|
||||
}
|
||||
|
||||
@@ -366,8 +365,9 @@ export const ProjectSidebarBody: Component<Props> = (props) => {
|
||||
onCreate={() => post({ type: "agentManager.createWorktree" })}
|
||||
onNew={() => props.onNewWorktree(props.project.id)}
|
||||
onSection={() => createSection()}
|
||||
onSetup={() => post({ type: "agentManager.configureSetupScript" })}
|
||||
onBranch={() => props.onDefaultBranch(props.project.id, state()?.defaultBaseBranch, props.local?.branch)}
|
||||
onSettings={() =>
|
||||
vscode.postMessage({ type: "openSettingsPanel", tab: "agentManager", projectId: props.project.id })
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -63,8 +63,6 @@ export interface SidebarBodyProps {
|
||||
onNewWorktree: () => void
|
||||
onNewSection: () => void
|
||||
onShortcuts: () => void
|
||||
onSetup: () => void
|
||||
onBranch: () => void
|
||||
sections: () => SectionState[]
|
||||
sortedWorktrees: () => WorktreeState[]
|
||||
worktrees: () => WorktreeState[]
|
||||
@@ -205,8 +203,9 @@ export const SidebarBody: Component<SidebarBodyProps> = (props) => {
|
||||
onNew={props.onNewWorktree}
|
||||
onSection={props.onNewSection}
|
||||
onShortcuts={props.onShortcuts}
|
||||
onSetup={props.onSetup}
|
||||
onBranch={props.onBranch}
|
||||
onSettings={() =>
|
||||
vscode.postMessage({ type: "openSettingsPanel", tab: "agentManager", projectId: props.projectId })
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -25,8 +25,7 @@ interface WorktreeSectionActionsProps {
|
||||
onNew: () => void
|
||||
onSection: () => void
|
||||
onShortcuts: () => void
|
||||
onSetup: () => void
|
||||
onBranch: () => void
|
||||
onSettings: () => void
|
||||
}
|
||||
|
||||
export const WorktreeSectionActions: Component<WorktreeSectionActionsProps> = (props) => (
|
||||
@@ -108,28 +107,13 @@ export const WorktreeSectionActions: Component<WorktreeSectionActionsProps> = (p
|
||||
onClick={props.onShortcuts}
|
||||
/>
|
||||
</TooltipKeybind>
|
||||
<DropdownMenu gutter={4} placement="bottom-end">
|
||||
<DropdownMenu.Trigger
|
||||
as={IconButton}
|
||||
icon="settings-gear"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
label={props.t("agentManager.worktree.settings")}
|
||||
/>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content class="am-split-menu">
|
||||
<DropdownMenu.Item onSelect={props.onSetup}>
|
||||
<DropdownMenu.ItemLabel>{props.t("agentManager.worktree.setupScript")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onSelect={props.onBranch}>
|
||||
<DropdownMenu.ItemLabel>
|
||||
{props.t("agentManager.worktree.defaultBaseBranch")}: {props.branch}
|
||||
</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu>
|
||||
<IconButton
|
||||
icon="settings-gear"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
label={props.t("agentManager.worktree.settings")}
|
||||
onClick={props.onSettings}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "الجلسات",
|
||||
"agentManager.projects": "المشاريع",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "المشروع",
|
||||
"agentManager.settings.project.description": "اختر repository الذي تريد تعديل إعدادات worktree الخاصة به.",
|
||||
"agentManager.settings.project.empty": "لا تتوفر أي مشاريع في Agent Manager.",
|
||||
"agentManager.settings.defaultBaseBranch.description": "branch المستخدم كنقطة بداية لـ worktrees جديدة.",
|
||||
"agentManager.settings.setupScript.description": "شغّل قبل أن يبدأ agent في worktree جديد.",
|
||||
"agentManager.settings.setupScript.create": "إنشاء script",
|
||||
"agentManager.settings.setupScript.edit": "تحرير script",
|
||||
"agentManager.project.add": "إضافة مشروع",
|
||||
"agentManager.project.remove": "إزالة من Agent Manager",
|
||||
"agentManager.project.missing": "المستودع غير موجود",
|
||||
|
||||
@@ -5,6 +5,15 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "SESSÕES",
|
||||
"agentManager.projects": "PROJETOS",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Projeto",
|
||||
"agentManager.settings.project.description":
|
||||
"Escolha o repository cujas configurações de worktree você deseja editar.",
|
||||
"agentManager.settings.project.empty": "Nenhum projeto do Agent Manager está disponível.",
|
||||
"agentManager.settings.defaultBaseBranch.description": "O branch usado como ponto de partida para novos worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Execute antes que um agent comece em um novo worktree.",
|
||||
"agentManager.settings.setupScript.create": "Criar script",
|
||||
"agentManager.settings.setupScript.edit": "Editar script",
|
||||
"agentManager.project.add": "Adicionar projeto",
|
||||
"agentManager.project.remove": "Remover do Agent Manager",
|
||||
"agentManager.project.missing": "Repositório não encontrado",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "SESIJE",
|
||||
"agentManager.projects": "PROJEKTI",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Projekat",
|
||||
"agentManager.settings.project.description": "Izaberite repository čije worktree postavke želite urediti.",
|
||||
"agentManager.settings.project.empty": "Nema dostupnih projekata u Agent Manager.",
|
||||
"agentManager.settings.defaultBaseBranch.description": "branch koji se koristi kao početna tačka za nove worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Pokrenite prije nego što agent počne u novom worktree.",
|
||||
"agentManager.settings.setupScript.create": "Kreiraj script",
|
||||
"agentManager.settings.setupScript.edit": "Uredi script",
|
||||
"agentManager.project.add": "Dodaj projekat",
|
||||
"agentManager.project.remove": "Ukloni iz Agent Manager-a",
|
||||
"agentManager.project.missing": "Repozitorij nije pronađen",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "SESSIONER",
|
||||
"agentManager.projects": "PROJEKTER",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Projekt",
|
||||
"agentManager.settings.project.description": "Vælg det repository, hvis worktree-indstillinger du vil redigere.",
|
||||
"agentManager.settings.project.empty": "Der er ingen tilgængelige projekter i Agent Manager.",
|
||||
"agentManager.settings.defaultBaseBranch.description": "Den branch, der bruges som udgangspunkt for nye worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Kør, før en agent starter i et nyt worktree.",
|
||||
"agentManager.settings.setupScript.create": "Opret script",
|
||||
"agentManager.settings.setupScript.edit": "Rediger script",
|
||||
"agentManager.project.add": "Tilføj projekt",
|
||||
"agentManager.project.remove": "Fjern fra Agent Manager",
|
||||
"agentManager.project.missing": "Repository ikke fundet",
|
||||
|
||||
@@ -5,6 +5,16 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "SITZUNGEN",
|
||||
"agentManager.projects": "PROJEKTE",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Projekt",
|
||||
"agentManager.settings.project.description":
|
||||
"Wählen Sie das repository aus, dessen worktree-Einstellungen Sie bearbeiten möchten.",
|
||||
"agentManager.settings.project.empty": "Keine Agent Manager-Projekte verfügbar.",
|
||||
"agentManager.settings.defaultBaseBranch.description":
|
||||
"Der branch, der als Ausgangspunkt für neue worktrees verwendet wird.",
|
||||
"agentManager.settings.setupScript.description": "Vor dem Start eines agent in einem neuen worktree ausführen.",
|
||||
"agentManager.settings.setupScript.create": "Script erstellen",
|
||||
"agentManager.settings.setupScript.edit": "Script bearbeiten",
|
||||
"agentManager.project.add": "Projekt hinzufügen",
|
||||
"agentManager.project.remove": "Aus Agent Manager entfernen",
|
||||
"agentManager.project.missing": "Repository nicht gefunden",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "SESSIONS",
|
||||
"agentManager.projects": "PROJECTS",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Project",
|
||||
"agentManager.settings.project.description": "Choose the repository whose worktree settings you want to edit.",
|
||||
"agentManager.settings.project.empty": "No Agent Manager projects are available.",
|
||||
"agentManager.settings.defaultBaseBranch.description": "The branch used as the starting point for new worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Run before an agent starts in a new worktree.",
|
||||
"agentManager.settings.setupScript.create": "Create script",
|
||||
"agentManager.settings.setupScript.edit": "Edit script",
|
||||
"agentManager.project.add": "Add project",
|
||||
"agentManager.project.remove": "Remove from Agent Manager",
|
||||
"agentManager.project.missing": "Repository not found",
|
||||
|
||||
@@ -5,6 +5,15 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "SESIONES",
|
||||
"agentManager.projects": "PROYECTOS",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Proyecto",
|
||||
"agentManager.settings.project.description": "Elige el repository cuyos ajustes de worktree quieres editar.",
|
||||
"agentManager.settings.project.empty": "No hay proyectos de Agent Manager disponibles.",
|
||||
"agentManager.settings.defaultBaseBranch.description":
|
||||
"El branch que se usa como punto de partida para nuevos worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Ejecutar antes de que un agent se inicie en un nuevo worktree.",
|
||||
"agentManager.settings.setupScript.create": "Crear script",
|
||||
"agentManager.settings.setupScript.edit": "Editar script",
|
||||
"agentManager.project.add": "Añadir proyecto",
|
||||
"agentManager.project.remove": "Eliminar de Agent Manager",
|
||||
"agentManager.project.missing": "Repositorio no encontrado",
|
||||
|
||||
@@ -5,6 +5,15 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "جلسات",
|
||||
"agentManager.projects": "پروژهها",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "پروژه",
|
||||
"agentManager.settings.project.description":
|
||||
"repository موردنظر را انتخاب کنید تا تنظیمات worktree آن را ویرایش کنید.",
|
||||
"agentManager.settings.project.empty": "هیچ پروژهای در Agent Manager در دسترس نیست.",
|
||||
"agentManager.settings.defaultBaseBranch.description": "branch مورد استفاده بهعنوان نقطه شروع برای worktrees جدید.",
|
||||
"agentManager.settings.setupScript.description": "قبل از شروع agent در یک worktree جدید اجرا شود.",
|
||||
"agentManager.settings.setupScript.create": "ایجاد script",
|
||||
"agentManager.settings.setupScript.edit": "ویرایش script",
|
||||
"agentManager.project.add": "افزودن پروژه",
|
||||
"agentManager.project.remove": "حذف از Agent Manager",
|
||||
"agentManager.project.missing": "مخزن یافت نشد",
|
||||
|
||||
@@ -5,6 +5,16 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "SESSIONS",
|
||||
"agentManager.projects": "PROJETS",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Projet",
|
||||
"agentManager.settings.project.description":
|
||||
"Choisissez le repository dont vous souhaitez modifier les paramètres du worktree.",
|
||||
"agentManager.settings.project.empty": "Aucun projet Agent Manager n'est disponible.",
|
||||
"agentManager.settings.defaultBaseBranch.description":
|
||||
"Le branch utilisé comme point de départ pour les nouveaux worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Exécuter avant qu'un agent ne démarre dans un nouveau worktree.",
|
||||
"agentManager.settings.setupScript.create": "Créer script",
|
||||
"agentManager.settings.setupScript.edit": "Modifier script",
|
||||
"agentManager.project.add": "Ajouter un projet",
|
||||
"agentManager.project.remove": "Retirer d'Agent Manager",
|
||||
"agentManager.project.missing": "Dépôt introuvable",
|
||||
|
||||
@@ -5,6 +5,16 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREE",
|
||||
"agentManager.section.sessions": "SESSIONI",
|
||||
"agentManager.projects": "PROGETTI",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Progetto",
|
||||
"agentManager.settings.project.description":
|
||||
"Scegli il repository di cui vuoi modificare le impostazioni del worktree.",
|
||||
"agentManager.settings.project.empty": "Non sono disponibili progetti di Agent Manager.",
|
||||
"agentManager.settings.defaultBaseBranch.description":
|
||||
"Il branch usato come punto di partenza per i nuovi worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Esegui prima che un agent inizi in un nuovo worktree.",
|
||||
"agentManager.settings.setupScript.create": "Crea script",
|
||||
"agentManager.settings.setupScript.edit": "Modifica script",
|
||||
"agentManager.project.add": "Aggiungi progetto",
|
||||
"agentManager.project.remove": "Rimuovi da Agent Manager",
|
||||
"agentManager.project.missing": "Repository non trovata",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "セッション",
|
||||
"agentManager.projects": "プロジェクト",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "プロジェクト",
|
||||
"agentManager.settings.project.description": "編集する worktree 設定の repository を選択してください。",
|
||||
"agentManager.settings.project.empty": "利用可能な Agent Manager プロジェクトはありません。",
|
||||
"agentManager.settings.defaultBaseBranch.description": "新しい worktrees の開始点として使用する branch。",
|
||||
"agentManager.settings.setupScript.description": "新しい worktree で agent が開始する前に実行します。",
|
||||
"agentManager.settings.setupScript.create": "script を作成",
|
||||
"agentManager.settings.setupScript.edit": "script を編集",
|
||||
"agentManager.project.add": "プロジェクトを追加",
|
||||
"agentManager.project.remove": "Agent Managerから削除",
|
||||
"agentManager.project.missing": "リポジトリが見つかりません",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "세션",
|
||||
"agentManager.projects": "프로젝트",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "프로젝트",
|
||||
"agentManager.settings.project.description": "편집하려는 worktree 설정의 repository를 선택하세요.",
|
||||
"agentManager.settings.project.empty": "사용 가능한 Agent Manager 프로젝트가 없습니다.",
|
||||
"agentManager.settings.defaultBaseBranch.description": "새 worktrees의 시작점으로 사용할 branch입니다.",
|
||||
"agentManager.settings.setupScript.description": "새 worktree에서 agent가 시작하기 전에 실행합니다.",
|
||||
"agentManager.settings.setupScript.create": "script 만들기",
|
||||
"agentManager.settings.setupScript.edit": "script 편집",
|
||||
"agentManager.project.add": "프로젝트 추가",
|
||||
"agentManager.project.remove": "Agent Manager에서 제거",
|
||||
"agentManager.project.missing": "저장소를 찾을 수 없음",
|
||||
|
||||
@@ -5,6 +5,15 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "SESSIES",
|
||||
"agentManager.projects": "PROJECTEN",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Project",
|
||||
"agentManager.settings.project.description": "Kies de repository waarvan je de worktree-instellingen wilt bewerken.",
|
||||
"agentManager.settings.project.empty": "Er zijn geen Agent Manager-projecten beschikbaar.",
|
||||
"agentManager.settings.defaultBaseBranch.description":
|
||||
"De branch die als startpunt voor nieuwe worktrees wordt gebruikt.",
|
||||
"agentManager.settings.setupScript.description": "Uitvoeren voordat een agent in een nieuwe worktree start.",
|
||||
"agentManager.settings.setupScript.create": "script maken",
|
||||
"agentManager.settings.setupScript.edit": "script bewerken",
|
||||
"agentManager.project.add": "Project toevoegen",
|
||||
"agentManager.project.remove": "Verwijderen uit Agent Manager",
|
||||
"agentManager.project.missing": "Repository niet gevonden",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "ØKTER",
|
||||
"agentManager.projects": "PROSJEKTER",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Prosjekt",
|
||||
"agentManager.settings.project.description": "Velg repository hvis worktree-innstillinger du vil redigere.",
|
||||
"agentManager.settings.project.empty": "Ingen Agent Manager-prosjekter er tilgjengelige.",
|
||||
"agentManager.settings.defaultBaseBranch.description": "branch som brukes som utgangspunkt for nye worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Kjør før en agent starter i et nytt worktree.",
|
||||
"agentManager.settings.setupScript.create": "Opprett script",
|
||||
"agentManager.settings.setupScript.edit": "Rediger script",
|
||||
"agentManager.project.add": "Legg til prosjekt",
|
||||
"agentManager.project.remove": "Fjern fra Agent Manager",
|
||||
"agentManager.project.missing": "Repository ikke funnet",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "SESJE",
|
||||
"agentManager.projects": "PROJEKTY",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Projekt",
|
||||
"agentManager.settings.project.description": "Wybierz repository, którego ustawienia worktree chcesz edytować.",
|
||||
"agentManager.settings.project.empty": "Brak dostępnych projektów Agent Manager.",
|
||||
"agentManager.settings.defaultBaseBranch.description": "branch używany jako punkt wyjścia dla nowych worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Uruchom przed tym, jak agent rozpocznie pracę w nowym worktree.",
|
||||
"agentManager.settings.setupScript.create": "Utwórz script",
|
||||
"agentManager.settings.setupScript.edit": "Edytuj script",
|
||||
"agentManager.project.add": "Dodaj projekt",
|
||||
"agentManager.project.remove": "Usuń z Agent Manager",
|
||||
"agentManager.project.missing": "Nie znaleziono repozytorium",
|
||||
|
||||
@@ -5,6 +5,15 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "СЕССИИ",
|
||||
"agentManager.projects": "ПРОЕКТЫ",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Проект",
|
||||
"agentManager.settings.project.description": "Выберите repository, настройки worktree которого хотите изменить.",
|
||||
"agentManager.settings.project.empty": "Нет доступных проектов Agent Manager.",
|
||||
"agentManager.settings.defaultBaseBranch.description":
|
||||
"branch, используемый как отправная точка для новых worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Запустите перед тем, как agent начнёт работу в новом worktree.",
|
||||
"agentManager.settings.setupScript.create": "Создать script",
|
||||
"agentManager.settings.setupScript.edit": "Изменить script",
|
||||
"agentManager.project.add": "Добавить проект",
|
||||
"agentManager.project.remove": "Удалить из Agent Manager",
|
||||
"agentManager.project.missing": "Репозиторий не найден",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "เซสชัน",
|
||||
"agentManager.projects": "โปรเจกต์",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "โปรเจกต์",
|
||||
"agentManager.settings.project.description": "เลือก repository ที่มีการตั้งค่า worktree ที่คุณต้องการแก้ไข",
|
||||
"agentManager.settings.project.empty": "ไม่มีโปรเจกต์ Agent Manager ที่พร้อมใช้งาน",
|
||||
"agentManager.settings.defaultBaseBranch.description": "branch ที่ใช้เป็นจุดเริ่มต้นสำหรับ worktrees ใหม่",
|
||||
"agentManager.settings.setupScript.description": "เรียกใช้ก่อนที่ agent จะเริ่มทำงานใน worktree ใหม่",
|
||||
"agentManager.settings.setupScript.create": "สร้าง script",
|
||||
"agentManager.settings.setupScript.edit": "แก้ไข script",
|
||||
"agentManager.project.add": "เพิ่มโปรเจกต์",
|
||||
"agentManager.project.remove": "ลบออกจาก Agent Manager",
|
||||
"agentManager.project.missing": "ไม่พบ Repository",
|
||||
|
||||
@@ -5,6 +5,15 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREE'LER",
|
||||
"agentManager.section.sessions": "OTURUMLAR",
|
||||
"agentManager.projects": "PROJELER",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Proje",
|
||||
"agentManager.settings.project.description": "Worktree ayarlarını düzenlemek istediğiniz repository'yi seçin.",
|
||||
"agentManager.settings.project.empty": "Kullanılabilir Agent Manager projesi yok.",
|
||||
"agentManager.settings.defaultBaseBranch.description":
|
||||
"Yeni worktrees için başlangıç noktası olarak kullanılan branch.",
|
||||
"agentManager.settings.setupScript.description": "Yeni bir worktree'de agent başlamadan önce çalıştırın.",
|
||||
"agentManager.settings.setupScript.create": "script oluştur",
|
||||
"agentManager.settings.setupScript.edit": "script düzenle",
|
||||
"agentManager.project.add": "Proje ekle",
|
||||
"agentManager.project.remove": "Agent Manager'dan kaldır",
|
||||
"agentManager.project.missing": "Depo bulunamadı",
|
||||
|
||||
@@ -5,6 +5,15 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "РОБОЧІ ДЕРЕВА",
|
||||
"agentManager.section.sessions": "СЕСІЇ",
|
||||
"agentManager.projects": "ПРОЄКТИ",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "Проєкт",
|
||||
"agentManager.settings.project.description": "Виберіть repository, налаштування worktree якого потрібно змінити.",
|
||||
"agentManager.settings.project.empty": "Немає доступних проєктів Agent Manager.",
|
||||
"agentManager.settings.defaultBaseBranch.description":
|
||||
"branch, який використовується як початкова точка для нових worktrees.",
|
||||
"agentManager.settings.setupScript.description": "Запускайте перед початком роботи agent у новому worktree.",
|
||||
"agentManager.settings.setupScript.create": "Створити script",
|
||||
"agentManager.settings.setupScript.edit": "Редагувати script",
|
||||
"agentManager.project.add": "Додати проєкт",
|
||||
"agentManager.project.remove": "Видалити з Agent Manager",
|
||||
"agentManager.project.missing": "Репозиторій не знайдено",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "会话",
|
||||
"agentManager.projects": "项目",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "项目",
|
||||
"agentManager.settings.project.description": "选择要编辑其 worktree 设置的 repository。",
|
||||
"agentManager.settings.project.empty": "没有可用的 Agent Manager 项目。",
|
||||
"agentManager.settings.defaultBaseBranch.description": "用于新 worktrees 起始点的 branch。",
|
||||
"agentManager.settings.setupScript.description": "在 agent 于新 worktree 中启动前运行。",
|
||||
"agentManager.settings.setupScript.create": "创建 script",
|
||||
"agentManager.settings.setupScript.edit": "编辑 script",
|
||||
"agentManager.project.add": "添加项目",
|
||||
"agentManager.project.remove": "从 Agent Manager 移除",
|
||||
"agentManager.project.missing": "未找到仓库",
|
||||
|
||||
@@ -5,6 +5,14 @@ export const dict = {
|
||||
"agentManager.section.worktrees": "WORKTREES",
|
||||
"agentManager.section.sessions": "工作階段",
|
||||
"agentManager.projects": "專案",
|
||||
"agentManager.settings.title": "Agent Manager",
|
||||
"agentManager.settings.project.title": "專案",
|
||||
"agentManager.settings.project.description": "選擇要編輯其 worktree 設定的 repository。",
|
||||
"agentManager.settings.project.empty": "沒有可用的 Agent Manager 專案。",
|
||||
"agentManager.settings.defaultBaseBranch.description": "用作新 worktrees 起始點的 branch。",
|
||||
"agentManager.settings.setupScript.description": "在 agent 於新的 worktree 中啟動前執行。",
|
||||
"agentManager.settings.setupScript.create": "建立 script",
|
||||
"agentManager.settings.setupScript.edit": "編輯 script",
|
||||
"agentManager.project.add": "新增專案",
|
||||
"agentManager.project.remove": "從 Agent Manager 移除",
|
||||
"agentManager.project.missing": "找不到儲存庫",
|
||||
|
||||
@@ -220,6 +220,7 @@ export const DataBridge: Component<{ children: any }> = (props) => {
|
||||
const AppContent: Component = () => {
|
||||
const [currentView, setCurrentView] = createSignal<ViewType>("newTask")
|
||||
const [settingsTab, setSettingsTab] = createSignal<string | undefined>()
|
||||
const [agentManagerProjectId, setAgentManagerProjectId] = createSignal<string | undefined>()
|
||||
// legacy-migration: state-driven flag independent of currentView to avoid
|
||||
// race conditions with SettingsEditorProvider's navigate messages.
|
||||
const [migrationNeeded, setMigrationNeeded] = createSignal(false)
|
||||
@@ -294,6 +295,7 @@ const AppContent: Component = () => {
|
||||
if (message?.type === "navigate" && message.view && VALID_VIEWS.has(message.view)) {
|
||||
console.log("[Kilo New] App: 🧭 navigate:", message.view, message.tab ? `tab=${message.tab}` : "")
|
||||
if (message.tab) setSettingsTab(message.tab)
|
||||
setAgentManagerProjectId(message.projectId)
|
||||
setCurrentView(message.view as ViewType)
|
||||
vscode.postMessage({ type: "settingsTabChanged", tab: message.tab })
|
||||
}
|
||||
@@ -341,7 +343,11 @@ const AppContent: Component = () => {
|
||||
// VS Code's native title bar toolbar already covers those. Defaults to
|
||||
// true only when unset entirely (e.g. Storybook, which doesn't render the
|
||||
// real page HTML).
|
||||
const host = window as { KILO_TOP_BAR?: boolean; KILO_TOP_BAR_SURFACE?: string }
|
||||
const host = window as {
|
||||
KILO_TOP_BAR?: boolean
|
||||
KILO_TOP_BAR_SURFACE?: string
|
||||
KILO_AGENT_MANAGER_SETTINGS?: boolean
|
||||
}
|
||||
const showTopBar = host.KILO_TOP_BAR !== false
|
||||
const topBarSurface = host.KILO_TOP_BAR_SURFACE ?? "sidebar_title"
|
||||
|
||||
@@ -396,6 +402,8 @@ const AppContent: Component = () => {
|
||||
<Match when={currentView() === "settings"}>
|
||||
<Settings
|
||||
tab={settingsTab()}
|
||||
agentManagerProjectId={agentManagerProjectId()}
|
||||
agentManagerSettings={host.KILO_AGENT_MANAGER_SETTINGS === true}
|
||||
onTabChange={setSettingsTab}
|
||||
onMigrationClick={(source) => {
|
||||
setMigrationSource(source)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Component, createSignal, createEffect, createMemo, on, Show } from "solid-js"
|
||||
import { Component, createSignal, createEffect, createMemo, on, Show, onCleanup } from "solid-js"
|
||||
import { Icon } from "@kilocode/kilo-ui/icon"
|
||||
import { Tabs } from "@kilocode/kilo-ui/tabs"
|
||||
import { Button } from "@kilocode/kilo-ui/button"
|
||||
import { useDialog } from "@kilocode/kilo-ui/context/dialog"
|
||||
import { Tooltip } from "@kilocode/kilo-ui/tooltip"
|
||||
import { showToast } from "@kilocode/kilo-ui/toast"
|
||||
import { useVSCode } from "../../context/vscode"
|
||||
@@ -29,13 +30,192 @@ import * as Sandboxing from "./sandboxing"
|
||||
import { useServer } from "../../context/server"
|
||||
import type { MigrationSource } from "../../types/messages"
|
||||
import { configMessage } from "../../utils/open-config"
|
||||
import type {
|
||||
AgentManagerSettingsBranchesLoadedMessage,
|
||||
AgentManagerSettingsLoadedMessage,
|
||||
AgentManagerSettingsProject,
|
||||
ExtensionMessage,
|
||||
} from "../../types/messages"
|
||||
import { Select } from "@kilocode/kilo-ui/select"
|
||||
import { Card } from "@kilocode/kilo-ui/card"
|
||||
import { Spinner } from "@kilocode/kilo-ui/spinner"
|
||||
import SettingsRow from "./SettingsRow"
|
||||
import { ProjectBranchDialog } from "../../../agent-manager/ProjectBranchDialog"
|
||||
|
||||
export interface SettingsProps {
|
||||
tab?: string
|
||||
agentManagerProjectId?: string
|
||||
agentManagerSettings?: boolean
|
||||
onTabChange?: (tab: string) => void
|
||||
onMigrationClick?: (source: MigrationSource) => void // legacy-migration
|
||||
}
|
||||
|
||||
const AgentManagerTab: Component<{ projectId?: string }> = (props) => {
|
||||
const language = useLanguage()
|
||||
const vscode = useVSCode()
|
||||
const dialog = useDialog()
|
||||
const [projects, setProjects] = createSignal<AgentManagerSettingsProject[]>([])
|
||||
const remembered = vscode.getState<{ agentManagerProjectId?: string }>()?.agentManagerProjectId
|
||||
const [selected, setSelected] = createSignal<string | undefined>(props.projectId ?? remembered)
|
||||
const [branches, setBranches] = createSignal<AgentManagerSettingsBranchesLoadedMessage>()
|
||||
const [branchLoading, setBranchLoading] = createSignal(false)
|
||||
const [loading, setLoading] = createSignal(true)
|
||||
let count = 0
|
||||
let latestSettings = ""
|
||||
let latestBranches = ""
|
||||
const issue = () => {
|
||||
return `${Date.now() * 1000 + ++count}`
|
||||
}
|
||||
const scriptPath = () =>
|
||||
projects().find((item) => item.id === selected())?.setupScriptPath ?? branches()?.setupScriptPath
|
||||
const load = (projectId?: string) => {
|
||||
if (!projectId) return
|
||||
setSelected(projectId)
|
||||
setBranches(undefined)
|
||||
setBranchLoading(false)
|
||||
vscode.setState({ ...vscode.getState<Record<string, unknown>>(), agentManagerProjectId: projectId })
|
||||
latestSettings = issue()
|
||||
latestBranches = latestSettings
|
||||
vscode.postMessage({ type: "requestAgentManagerSettings", projectId, requestId: latestSettings })
|
||||
}
|
||||
const loadBranches = (projectId?: string) => {
|
||||
if (!projectId || (branches()?.projectId === projectId && !branches()?.error) || branchLoading()) return
|
||||
setBranchLoading(true)
|
||||
latestBranches = issue()
|
||||
vscode.postMessage({ type: "requestAgentManagerSettingsBranches", projectId, requestId: latestBranches })
|
||||
}
|
||||
createEffect(() => {
|
||||
if (props.projectId && props.projectId !== selected()) load(props.projectId)
|
||||
})
|
||||
const onMessage = (message: ExtensionMessage) => {
|
||||
if (message.type === "agentManagerSettingsLoaded") {
|
||||
const event = message as AgentManagerSettingsLoadedMessage
|
||||
if (event.requestId !== latestSettings) return
|
||||
setProjects(event.projects)
|
||||
const current = selected()
|
||||
const next = event.projects.some((project) => project.id === current) ? current : event.projectId
|
||||
setSelected(next)
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
if (message.type === "agentManagerSettingsBranchesLoaded") {
|
||||
const event = message as AgentManagerSettingsBranchesLoadedMessage
|
||||
if (event.requestId !== latestBranches) return
|
||||
if (event.projectId === selected()) {
|
||||
setBranches(event.error ? undefined : event)
|
||||
setBranchLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
const initial = props.projectId ?? remembered
|
||||
const timeout = setTimeout(() => setLoading(false), 3000)
|
||||
const unsubscribe = vscode.onMessage(onMessage)
|
||||
onCleanup(() => {
|
||||
unsubscribe()
|
||||
clearTimeout(timeout)
|
||||
})
|
||||
latestSettings = issue()
|
||||
vscode.postMessage({ type: "requestAgentManagerSettings", projectId: initial, requestId: latestSettings })
|
||||
const project = () => projects().find((item) => item.id === selected())
|
||||
const projectText = (item: AgentManagerSettingsProject) => `${item.label} - ${item.root}`
|
||||
const branchLabel = () => {
|
||||
const data = branches()
|
||||
const configured = data ? data.configuredBaseBranch : project()?.defaultBaseBranch
|
||||
if (configured) return configured
|
||||
const detected = data?.defaultBranch ?? project()?.defaultBranch
|
||||
return `${language.t("agentManager.worktree.defaultBaseBranchAuto")}${detected ? ` (${detected})` : ""}`
|
||||
}
|
||||
const setBranch = (branch?: string) => {
|
||||
const id = selected()
|
||||
if (!id) return
|
||||
setBranches((current) => (current ? { ...current, configuredBaseBranch: branch } : current))
|
||||
latestSettings = issue()
|
||||
latestBranches = latestSettings
|
||||
vscode.postMessage({ type: "setAgentManagerDefaultBaseBranch", projectId: id, branch, requestId: latestSettings })
|
||||
}
|
||||
const openBranches = () => {
|
||||
const id = selected()
|
||||
if (!id || project()?.missing) return
|
||||
loadBranches(id)
|
||||
dialog.show(() => (
|
||||
<ProjectBranchDialog
|
||||
selected={branches()?.configuredBaseBranch ?? project()?.defaultBaseBranch}
|
||||
detected={branches()?.defaultBranch ?? project()?.defaultBranch}
|
||||
branches={() => branches()?.branches ?? []}
|
||||
loading={branchLoading}
|
||||
onClose={() => dialog.close()}
|
||||
onSelect={setBranch}
|
||||
/>
|
||||
))
|
||||
}
|
||||
return (
|
||||
<Show when={!loading()} fallback={<Spinner />}>
|
||||
<div class="settings-agent-manager">
|
||||
<Card>
|
||||
<SettingsRow
|
||||
title={language.t("agentManager.settings.project.title")}
|
||||
description={language.t("agentManager.settings.project.description")}
|
||||
>
|
||||
<Select
|
||||
options={projects()}
|
||||
current={project()}
|
||||
value={(item) => item.id}
|
||||
label={projectText}
|
||||
onSelect={(item) => load(item?.id)}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
triggerVariant="settings"
|
||||
triggerProps={{ title: project()?.root }}
|
||||
children={(item) => (item ? <span title={projectText(item)}>{projectText(item)}</span> : "")}
|
||||
/>
|
||||
</SettingsRow>
|
||||
<Show when={project()} fallback={<p>{language.t("agentManager.settings.project.empty")}</p>}>
|
||||
<SettingsRow
|
||||
title={language.t("agentManager.worktree.defaultBaseBranch")}
|
||||
description={language.t("agentManager.settings.defaultBaseBranch.description")}
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
class="settings-branch-trigger"
|
||||
title={branchLabel()}
|
||||
disabled={project()?.missing || branchLoading()}
|
||||
onClick={openBranches}
|
||||
>
|
||||
<span class="settings-branch-trigger-label">{branchLabel()}</span>
|
||||
<Icon name="selector" size="small" />
|
||||
</Button>
|
||||
</SettingsRow>
|
||||
<SettingsRow
|
||||
title={language.t("agentManager.worktree.setupScript")}
|
||||
description={language.t("agentManager.settings.setupScript.description")}
|
||||
last
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
icon="edit"
|
||||
disabled={project()?.missing}
|
||||
onClick={() =>
|
||||
vscode.postMessage({
|
||||
type: "configureAgentManagerSetupScript",
|
||||
projectId: selected()!,
|
||||
requestId: (latestSettings = issue()),
|
||||
})
|
||||
}
|
||||
>
|
||||
{scriptPath()
|
||||
? language.t("agentManager.settings.setupScript.edit")
|
||||
: language.t("agentManager.settings.setupScript.create")}
|
||||
</Button>
|
||||
</SettingsRow>
|
||||
</Show>
|
||||
</Card>
|
||||
</div>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
const Settings: Component<SettingsProps> = (props) => {
|
||||
const server = useServer()
|
||||
const language = useLanguage()
|
||||
@@ -151,6 +331,12 @@ const Settings: Component<SettingsProps> = (props) => {
|
||||
<Icon name="checklist" />
|
||||
<span class="label">{language.t("settings.autoApprove.title")}</span>
|
||||
</Tabs.Trigger>
|
||||
<Show when={props.agentManagerSettings}>
|
||||
<Tabs.Trigger value="agentManager" aria-label={language.t("agentManager.settings.title")}>
|
||||
<Icon name="settings-gear" />
|
||||
<span class="label">{language.t("agentManager.settings.title")}</span>
|
||||
</Tabs.Trigger>
|
||||
</Show>
|
||||
<Tabs.Trigger value="browser" aria-label={language.t("settings.webTools.title")}>
|
||||
<Icon name="window-cursor" />
|
||||
<span class="label">{language.t("settings.webTools.title")}</span>
|
||||
@@ -222,6 +408,12 @@ const Settings: Component<SettingsProps> = (props) => {
|
||||
<h3>{language.t("settings.autoApprove.title")}</h3>
|
||||
<AutoApproveTab />
|
||||
</Tabs.Content>
|
||||
<Show when={props.agentManagerSettings}>
|
||||
<Tabs.Content value="agentManager">
|
||||
<h3>{language.t("agentManager.settings.title")}</h3>
|
||||
<AgentManagerTab projectId={props.agentManagerProjectId} />
|
||||
</Tabs.Content>
|
||||
</Show>
|
||||
<Tabs.Content value="browser">
|
||||
<h3>{language.t("settings.webTools.title")}</h3>
|
||||
<BrowserTab />
|
||||
|
||||
@@ -115,6 +115,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.settings-branch-trigger {
|
||||
max-width: 160px;
|
||||
}
|
||||
|
||||
.settings-branch-trigger-label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Wider inputs inside cards with data-variant="wide-input" (e.g. edit mode config) */
|
||||
[data-variant="wide-input"] [data-slot="settings-row-input"] {
|
||||
flex: 1 1 0%;
|
||||
|
||||
@@ -411,6 +411,36 @@ export interface NavigateMessage {
|
||||
type: "navigate"
|
||||
view: "newTask" | "marketplace" | "history" | "profile" | "settings" | "subAgentViewer"
|
||||
tab?: string
|
||||
projectId?: string
|
||||
}
|
||||
|
||||
export interface AgentManagerSettingsProject {
|
||||
id: string
|
||||
root: string
|
||||
label: string
|
||||
pinned: boolean
|
||||
missing: boolean
|
||||
defaultBaseBranch?: string
|
||||
defaultBranch?: string
|
||||
setupScriptPath?: string
|
||||
}
|
||||
|
||||
export interface AgentManagerSettingsLoadedMessage {
|
||||
type: "agentManagerSettingsLoaded"
|
||||
projects: AgentManagerSettingsProject[]
|
||||
projectId?: string
|
||||
requestId: string
|
||||
}
|
||||
|
||||
export interface AgentManagerSettingsBranchesLoadedMessage {
|
||||
type: "agentManagerSettingsBranchesLoaded"
|
||||
projectId: string
|
||||
branches: BranchInfo[]
|
||||
defaultBranch: string
|
||||
requestId: string
|
||||
error?: boolean
|
||||
configuredBaseBranch?: string
|
||||
setupScriptPath?: string
|
||||
}
|
||||
|
||||
export interface IndexingStatusLoadedMessage {
|
||||
@@ -1420,6 +1450,8 @@ export type ExtensionMessage =
|
||||
| DeviceAuthFailedMessage
|
||||
| DeviceAuthCancelledMessage
|
||||
| NavigateMessage
|
||||
| AgentManagerSettingsLoadedMessage
|
||||
| AgentManagerSettingsBranchesLoadedMessage
|
||||
| IndexingStatusLoadedMessage
|
||||
| IndexingSettingsLoadedMessage
|
||||
| ChatSettingsLoadedMessage
|
||||
|
||||
@@ -234,6 +234,32 @@ export interface CompactRequest {
|
||||
export interface OpenSettingsPanelRequest {
|
||||
type: "openSettingsPanel"
|
||||
tab?: string
|
||||
projectId?: string
|
||||
}
|
||||
|
||||
export interface RequestAgentManagerSettingsMessage {
|
||||
type: "requestAgentManagerSettings"
|
||||
projectId?: string
|
||||
requestId: string
|
||||
}
|
||||
|
||||
export interface RequestAgentManagerSettingsBranchesMessage {
|
||||
type: "requestAgentManagerSettingsBranches"
|
||||
projectId: string
|
||||
requestId: string
|
||||
}
|
||||
|
||||
export interface SetAgentManagerDefaultBaseBranchMessage {
|
||||
type: "setAgentManagerDefaultBaseBranch"
|
||||
projectId: string
|
||||
branch?: string
|
||||
requestId: string
|
||||
}
|
||||
|
||||
export interface ConfigureAgentManagerSetupScriptMessage {
|
||||
type: "configureAgentManagerSetupScript"
|
||||
projectId: string
|
||||
requestId: string
|
||||
}
|
||||
|
||||
export interface OpenProfilePanelRequest {
|
||||
@@ -1488,6 +1514,10 @@ export type WebviewMessage =
|
||||
| RefreshProviderUsageMessage
|
||||
| OpenExternalRequest
|
||||
| OpenSettingsPanelRequest
|
||||
| RequestAgentManagerSettingsMessage
|
||||
| RequestAgentManagerSettingsBranchesMessage
|
||||
| SetAgentManagerDefaultBaseBranchMessage
|
||||
| ConfigureAgentManagerSetupScriptMessage
|
||||
| OpenProfilePanelRequest
|
||||
| OpenVSCodeSettingsRequest
|
||||
| OpenConfigFileRequest
|
||||
|
||||
@@ -244,11 +244,15 @@ import type {
|
||||
McpAuthRemoveResponses,
|
||||
McpAuthStartErrors,
|
||||
McpAuthStartResponses,
|
||||
McpCallToolErrors,
|
||||
McpCallToolResponses,
|
||||
McpConnectErrors,
|
||||
McpConnectResponses,
|
||||
McpDisconnectErrors,
|
||||
McpDisconnectResponses,
|
||||
McpLocalConfig,
|
||||
McpReadResourceErrors,
|
||||
McpReadResourceResponses,
|
||||
McpRemoteConfig,
|
||||
McpStatusErrors,
|
||||
McpStatusResponses,
|
||||
@@ -3184,6 +3188,88 @@ export class Mcp extends HeyApiClient {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Read MCP resource
|
||||
*
|
||||
* Read a resource from a connected MCP server by URI. Used by MCP Apps to load UI resources.
|
||||
*/
|
||||
public readResource<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
uri?: string
|
||||
server?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
{ in: "body", key: "uri" },
|
||||
{ in: "body", key: "server" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<McpReadResourceResponses, McpReadResourceErrors, ThrowOnError>({
|
||||
url: "/experimental/resource/read",
|
||||
...options,
|
||||
...params,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
...params.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Call MCP tool
|
||||
*
|
||||
* Call a tool on a connected MCP server. Used by MCP Apps for widget-initiated tool calls.
|
||||
*/
|
||||
public callTool<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
server?: string
|
||||
name?: string
|
||||
arguments?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
{ in: "body", key: "server" },
|
||||
{ in: "body", key: "name" },
|
||||
{ in: "body", key: "arguments" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<McpCallToolResponses, McpCallToolErrors, ThrowOnError>({
|
||||
url: "/experimental/mcp/call-tool",
|
||||
...options,
|
||||
...params,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
...params.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
private _auth?: Auth2
|
||||
get auth(): Auth2 {
|
||||
return (this._auth ??= new Auth2({ client: this.client }))
|
||||
|
||||
@@ -12124,6 +12124,90 @@ export type McpDisconnectResponses = {
|
||||
|
||||
export type McpDisconnectResponse = McpDisconnectResponses[keyof McpDisconnectResponses]
|
||||
|
||||
export type McpReadResourceData = {
|
||||
body?: {
|
||||
uri: string
|
||||
server: string
|
||||
}
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/experimental/resource/read"
|
||||
}
|
||||
|
||||
export type McpReadResourceErrors = {
|
||||
/**
|
||||
* BadRequest | InvalidRequestError
|
||||
*/
|
||||
400: EffectHttpApiErrorBadRequest | InvalidRequestError
|
||||
/**
|
||||
* Not found
|
||||
*/
|
||||
404: NotFoundError
|
||||
}
|
||||
|
||||
export type McpReadResourceError = McpReadResourceErrors[keyof McpReadResourceErrors]
|
||||
|
||||
export type McpReadResourceResponses = {
|
||||
/**
|
||||
* Resource content
|
||||
*/
|
||||
200: {
|
||||
uri: string
|
||||
mimeType?: string
|
||||
text?: string
|
||||
blob?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type McpReadResourceResponse = McpReadResourceResponses[keyof McpReadResourceResponses]
|
||||
|
||||
export type McpCallToolData = {
|
||||
body?: {
|
||||
server: string
|
||||
name: string
|
||||
arguments?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/experimental/mcp/call-tool"
|
||||
}
|
||||
|
||||
export type McpCallToolErrors = {
|
||||
/**
|
||||
* BadRequest | InvalidRequestError
|
||||
*/
|
||||
400: EffectHttpApiErrorBadRequest | InvalidRequestError
|
||||
/**
|
||||
* Not found
|
||||
*/
|
||||
404: NotFoundError
|
||||
}
|
||||
|
||||
export type McpCallToolError = McpCallToolErrors[keyof McpCallToolErrors]
|
||||
|
||||
export type McpCallToolResponses = {
|
||||
/**
|
||||
* Tool call result
|
||||
*/
|
||||
200: {
|
||||
content: Array<unknown>
|
||||
isError?: boolean
|
||||
structuredContent?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type McpCallToolResponse = McpCallToolResponses[keyof McpCallToolResponses]
|
||||
|
||||
export type ProjectListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
|
||||
Reference in New Issue
Block a user