diff --git a/src/vs/agentic/contrib/aiCustomizationManagement/browser/SPEC.md b/src/vs/agentic/contrib/aiCustomizationManagement/browser/SPEC.md index d8553d7a2c8..afd540af605 100644 --- a/src/vs/agentic/contrib/aiCustomizationManagement/browser/SPEC.md +++ b/src/vs/agentic/contrib/aiCustomizationManagement/browser/SPEC.md @@ -80,10 +80,10 @@ aiCustomizationManagement/browser/ - Fuzzy matches across name, description, and filename. - Debounced (200ms) filtering. -**Active session filtering:** -- Filters worktree items to the active session repository/worktree. - - The active worktree comes from `IActiveAgentSessionService` and is the source of truth for scoping. +**Active session scoping:** +- The active worktree comes from `IActiveAgentSessionService` and is the source of truth for scoping. - Prompt discovery is scoped by the agentic prompt service override using the active session root. +- Views refresh counts/filters when the active session changes. **Context menu actions:** - Open, Run Prompt (prompts), Reveal in OS, Delete. diff --git a/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationListWidget.ts b/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationListWidget.ts index d102bfce6d4..8ff4318dc70 100644 --- a/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationListWidget.ts +++ b/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationListWidget.ts @@ -8,6 +8,7 @@ import * as DOM from '../../../../base/browser/dom.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { autorun } from '../../../../base/common/observable.js'; import { basename, dirname } from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; @@ -38,7 +39,6 @@ import { ILabelService } from '../../../../platform/label/common/label.js'; import { parseAllHookFiles } from '../../../../workbench/contrib/chat/browser/promptSyntax/hookUtils.js'; import { OS } from '../../../../base/common/platform.js'; import { IRemoteAgentService } from '../../../../workbench/services/remote/common/remoteAgentService.js'; -import { autorun } from '../../../../base/common/observable.js'; import { IActiveAgentSessionService } from '../../../../workbench/contrib/chat/browser/agentSessions/agentSessionsService.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { Action, Separator } from '../../../../base/common/actions.js'; @@ -350,6 +350,11 @@ export class AICustomizationListWidget extends Disposable { this.create(); this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.refresh())); + this._register(autorun(reader => { + this.activeSessionService.activeSession.read(reader); + this.updateAddButton(); + this.refresh(); + })); // Re-filter when SCM repositories change (updates git status badges after commits) const trackRepoChanges = (repo: { provider: { onDidChangeResources: Event } }) => { @@ -363,22 +368,6 @@ export class AICustomizationListWidget extends Disposable { } this._register(this.scmService.onDidAddRepository(repo => trackRepoChanges(repo))); - // React immediately when the active agent session changes. - // filterItems() is synchronous and re-groups/filters existing allItems instantly. - // Also kick off an async refresh to pick up items from the new repo. - this._register(autorun(reader => { - this.activeSessionService.activeSession.read(reader); - const activePath = getActiveSessionRoot(this.activeSessionService); - this.logService.info(`[AICustomizationListWidget] Active session changed, worktree/repo: ${activePath?.toString() ?? 'none'}, allItems=${this.allItems.length}`); - // Update the add button since worktree availability may have changed - this.updateAddButton(); - // Immediate synchronous re-filter for instant UI update - if (this.allItems.length > 0) { - this.filterItems(); - } - // Also do a full async reload to pick up new workspace items - void this.refresh(); - })); } private create(): void { @@ -623,13 +612,10 @@ export class AICustomizationListWidget extends Disposable { } /** - * Checks if there's an active worktree from a background agent session. - * Only returns true for sessions that have an actual git worktree - * (not just a repository option from the workspace). + * Checks if there's an active session root (worktree or repository). */ private hasActiveWorktree(): boolean { - const session = this.activeSessionService.getActiveSession(); - return !!(session?.worktree); + return !!getActiveSessionRoot(this.activeSessionService); } /** diff --git a/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationManagementEditor.ts b/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationManagementEditor.ts index bc4d97b639c..a1597d2c949 100644 --- a/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationManagementEditor.ts +++ b/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationManagementEditor.ts @@ -8,6 +8,7 @@ import * as DOM from '../../../../base/browser/dom.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { DisposableStore, IReference, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { Event } from '../../../../base/common/event.js'; +import { autorun } from '../../../../base/common/observable.js'; import { Orientation, Sizing, SplitView } from '../../../../base/browser/ui/splitview/splitview.js'; import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; @@ -148,6 +149,7 @@ export class AICustomizationManagementEditor extends EditorPane { private editorItemPathElement!: HTMLElement; private currentEditingUri: URI | undefined; private currentWorktreeUri: URI | undefined; + private currentEditingIsWorktree = false; private currentModelRef: IReference | undefined; private viewMode: 'list' | 'editor' = 'list'; @@ -186,6 +188,14 @@ export class AICustomizationManagementEditor extends EditorPane { this.customizationCreator = this.instantiationService.createInstance(CustomizationCreatorService); + this._register(autorun(reader => { + this.activeAgentSessionService.activeSession.read(reader); + if (this.viewMode !== 'editor' || !this.currentEditingIsWorktree) { + return; + } + this.currentWorktreeUri = getActiveSessionRoot(this.activeAgentSessionService); + })); + // Safety disposal for the embedded editor model reference this._register(toDisposable(() => { this.currentModelRef?.dispose(); @@ -537,6 +547,7 @@ export class AICustomizationManagementEditor extends EditorPane { // Track worktree URI for auto-commit on close const worktreeDir = getActiveSessionRoot(this.activeAgentSessionService); this.currentWorktreeUri = isWorktreeFile ? worktreeDir : undefined; + this.currentEditingIsWorktree = isWorktreeFile; // Update visibility this.updateContentVisibility(); @@ -578,6 +589,7 @@ export class AICustomizationManagementEditor extends EditorPane { this.currentModelRef = undefined; this.currentEditingUri = undefined; this.currentWorktreeUri = undefined; + this.currentEditingIsWorktree = false; // Clear editor model this.embeddedEditor.setModel(null); diff --git a/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationOverviewView.ts b/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationOverviewView.ts index a2fc0ceb930..d742d283919 100644 --- a/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationOverviewView.ts +++ b/src/vs/agentic/contrib/aiCustomizationManagement/browser/aiCustomizationOverviewView.ts @@ -6,6 +6,7 @@ import './media/aiCustomizationManagement.css'; import * as DOM from '../../../../base/browser/dom.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { autorun } from '../../../../base/common/observable.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { localize } from '../../../../nls.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; @@ -26,7 +27,6 @@ import { AICustomizationManagementEditorInput } from './aiCustomizationManagemen import { AICustomizationManagementEditor } from './aiCustomizationManagementEditor.js'; import { agentIcon, instructionsIcon, promptIcon, skillIcon } from '../../aiCustomizationTreeView/browser/aiCustomizationTreeViewIcons.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { autorun } from '../../../../base/common/observable.js'; import { IActiveAgentSessionService } from '../../../../workbench/contrib/chat/browser/agentSessions/agentSessionsService.js'; const $ = DOM.$; @@ -84,12 +84,11 @@ export class AICustomizationOverviewView extends ViewPane { // Listen to workspace folder changes to update counts this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.loadCounts())); - - // Refresh when active agent session changes (repository scope may change) this._register(autorun(reader => { this.activeSessionService.activeSession.read(reader); - void this.loadCounts(); + this.loadCounts(); })); + } protected override renderBody(container: HTMLElement): void { diff --git a/src/vs/agentic/contrib/aiCustomizationTreeView/browser/SPEC.md b/src/vs/agentic/contrib/aiCustomizationTreeView/browser/SPEC.md index 8e503da9fb7..c3612b7e4d3 100644 --- a/src/vs/agentic/contrib/aiCustomizationTreeView/browser/SPEC.md +++ b/src/vs/agentic/contrib/aiCustomizationTreeView/browser/SPEC.md @@ -55,9 +55,8 @@ aiCustomizationTreeView/browser/ - Creates the tree and renderers. - Auto-expands categories on load/refresh. - Refreshes on prompt service changes, workspace changes, and active session changes. -- Filters worktree items to the active session repository. - - Updates `aiCustomization.isEmpty` based on total item count. - - The active worktree comes from `IActiveAgentSessionService` and is the source of truth for scoping. +- Updates `aiCustomization.isEmpty` based on total item count. +- Worktree scoping comes from the agentic prompt service override. ### UnifiedAICustomizationDataSource diff --git a/src/vs/agentic/contrib/aiCustomizationTreeView/browser/aiCustomizationTreeViewViews.ts b/src/vs/agentic/contrib/aiCustomizationTreeView/browser/aiCustomizationTreeViewViews.ts index b3212f1bb1d..c8f70d2704c 100644 --- a/src/vs/agentic/contrib/aiCustomizationTreeView/browser/aiCustomizationTreeViewViews.ts +++ b/src/vs/agentic/contrib/aiCustomizationTreeView/browser/aiCustomizationTreeViewViews.ts @@ -7,6 +7,7 @@ import './media/aiCustomizationTreeView.css'; import * as dom from '../../../../base/browser/dom.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../base/common/observable.js'; import { basename, dirname } from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; @@ -34,7 +35,6 @@ import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; import { IEditorService } from '../../../../workbench/services/editor/common/editorService.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { autorun } from '../../../../base/common/observable.js'; import { IActiveAgentSessionService } from '../../../../workbench/contrib/chat/browser/agentSessions/agentSessionsService.js'; //#region Context Keys @@ -501,12 +501,11 @@ export class AICustomizationViewPane extends ViewPane { // Listen to workspace folder changes to refresh tree this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.refresh())); - - // Refresh when active agent session changes (repository scope may change) this._register(autorun(reader => { this.activeSessionService.activeSession.read(reader); - void this.refresh(); + this.refresh(); })); + } protected override renderBody(container: HTMLElement): void { diff --git a/src/vs/agentic/contrib/sessionsView/browser/sessionsViewPane.ts b/src/vs/agentic/contrib/sessionsView/browser/sessionsViewPane.ts index 60d7486dfbc..c922d61116f 100644 --- a/src/vs/agentic/contrib/sessionsView/browser/sessionsViewPane.ts +++ b/src/vs/agentic/contrib/sessionsView/browser/sessionsViewPane.ts @@ -121,12 +121,11 @@ export class AgenticSessionsViewPane extends ViewPane { // Listen to workspace folder changes to update counts this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.updateCounts())); - - // Listen to active session changes to update counts (repository scope may change) this._register(autorun(reader => { this.activeSessionService.activeSession.read(reader); - void this.updateCounts(); + this.updateCounts(); })); + } protected override renderBody(parent: HTMLElement): void {