summaryrefslogtreecommitdiff
path: root/packages/core/src/utils/workspaceContext.ts
diff options
context:
space:
mode:
authorJacob MacDonald <[email protected]>2025-08-18 14:09:02 -0700
committerGitHub <[email protected]>2025-08-18 21:09:02 +0000
commit3960ccf78197c140ee483fccd654680894cdea47 (patch)
treec6533f9afb4384ed8d7c0411adcaa1c98d3d4b20 /packages/core/src/utils/workspaceContext.ts
parent465ac9f547d0d684439886d1466c1a1133da611d (diff)
Add MCP Root change notifications (#6502)
Diffstat (limited to 'packages/core/src/utils/workspaceContext.ts')
-rw-r--r--packages/core/src/utils/workspaceContext.ts46
1 files changed, 43 insertions, 3 deletions
diff --git a/packages/core/src/utils/workspaceContext.ts b/packages/core/src/utils/workspaceContext.ts
index be99a83c..924cd601 100644
--- a/packages/core/src/utils/workspaceContext.ts
+++ b/packages/core/src/utils/workspaceContext.ts
@@ -8,6 +8,8 @@ import { isNodeError } from '../utils/errors.js';
import * as fs from 'fs';
import * as path from 'path';
+export type Unsubscribe = () => void;
+
/**
* WorkspaceContext manages multiple workspace directories and validates paths
* against them. This allows the CLI to operate on files from multiple directories
@@ -16,6 +18,7 @@ import * as path from 'path';
export class WorkspaceContext {
private directories = new Set<string>();
private initialDirectories: Set<string>;
+ private onDirectoriesChangedListeners = new Set<() => void>();
/**
* Creates a new WorkspaceContext with the given initial directory and optional additional directories.
@@ -32,12 +35,41 @@ export class WorkspaceContext {
}
/**
+ * Registers a listener that is called when the workspace directories change.
+ * @param listener The listener to call.
+ * @returns A function to unsubscribe the listener.
+ */
+ onDirectoriesChanged(listener: () => void): Unsubscribe {
+ this.onDirectoriesChangedListeners.add(listener);
+ return () => {
+ this.onDirectoriesChangedListeners.delete(listener);
+ };
+ }
+
+ private notifyDirectoriesChanged() {
+ // Iterate over a copy of the set in case a listener unsubscribes itself or others.
+ for (const listener of [...this.onDirectoriesChangedListeners]) {
+ try {
+ listener();
+ } catch (e) {
+ // Don't let one listener break others.
+ console.error('Error in WorkspaceContext listener:', e);
+ }
+ }
+ }
+
+ /**
* Adds a directory to the workspace.
* @param directory The directory path to add (can be relative or absolute)
* @param basePath Optional base path for resolving relative paths (defaults to cwd)
*/
addDirectory(directory: string, basePath: string = process.cwd()): void {
- this.directories.add(this.resolveAndValidateDir(directory, basePath));
+ const resolved = this.resolveAndValidateDir(directory, basePath);
+ if (this.directories.has(resolved)) {
+ return;
+ }
+ this.directories.add(resolved);
+ this.notifyDirectoriesChanged();
}
private resolveAndValidateDir(
@@ -72,9 +104,17 @@ export class WorkspaceContext {
}
setDirectories(directories: readonly string[]): void {
- this.directories.clear();
+ const newDirectories = new Set<string>();
for (const dir of directories) {
- this.addDirectory(dir);
+ newDirectories.add(this.resolveAndValidateDir(dir));
+ }
+
+ if (
+ newDirectories.size !== this.directories.size ||
+ ![...newDirectories].every((d) => this.directories.has(d))
+ ) {
+ this.directories = newDirectories;
+ this.notifyDirectoriesChanged();
}
}