@builder.io/dev-tools
Version:
Builder.io Visual CMS Devtools
787 lines (786 loc) • 36 kB
TypeScript
import type { DevToolsSys } from "../types";
import { type Credentials } from "./credentials";
import { TimelineCollector } from "./utils/timeline-collector";
import type { CodegenFeedback, CodeGenToolMap, CodegenTurn, CustomInstruction, FusionConfig, GenerateCompletionState, GenerateCompletionStep, GenerateUserMessage, SessionMode, UserContext, UserSource, WorkspaceFolder, LoadWholeSessionOptions, LoadWholeSessionResult, LoadHistoryResult, ApplyActionsResult, PrivacyMode, CodeGenPosition, BackupGitRepoResult, SuggestedActionBuildError, PushChangesArgs, CodegenApiResult, CodegenApiTerminal, ConfigureDevOrchestratorOpts, ConfigureDevOrchestratorUpdates, RepoMetrics, FolderWatchEvent, MCPClientStatus, MCPServerConfig, CodegenApiCreateTerminal, SyncChangesFromRemote, SearchFilesOptions, SearchFilesResult, SearchFileTreeOptions, SearchFileTreeResult, HttpServerState, ExplorationMetadataToolInput, CodegenAbortOptions, MessageUpdateOptions, GitRepoContext, SystemReminderObj, GitSnapshot, AutoPushMode, QueueMode, CustomAgentDefinition, AgentModelOverrides, MemorySummary, SendMessageToOrgAgentInput, ReasoningEffort } from "#ai-utils";
import { type ToolResolution } from "./code-tools";
import { type SubAgent } from "./utils/agent-discovery";
import EventEmitter from "node:events";
import { type McpScope } from "./mcp-manage";
import { type RunCommandOptions } from "./utils/git";
import { type DevServerOrchestrator } from "./launch/dev-server-orchestrator";
import type { FusionStatusMonitor } from "./launch/machine-health";
export interface SyncChangesFromBranches {
canPush: boolean;
branches: string[];
uncommittedChanges: "stash" | "commit" | "fail";
allowUnrelatedHistory: boolean;
fastForward: "never" | "required" | "auto";
requestRefresh?: boolean;
/**
* When true, compute the remote branch per-repo using #getRemoteBranch(ctx)
* instead of using the passed branches array. This handles multi-repo
* scenarios where each repo may have a different feature branch.
*/
syncRemoteBranch?: boolean;
/**
* When true, perform a hard reset to the remote feature/base branch instead
* of merging. The `branches` array must contain the feature/base branch name.
*/
resetToBase?: boolean;
/**
* Whether to update the internal "last AI commits" baseline after the sync
* completes. Defaults to `true`. When `false`, `#lastAICommits` is left
* untouched so `getChangesReport()` keeps diffing from the previous
* baseline (useful to surface what changed since the reset in a prompt).
*/
updateLastCommits?: boolean;
}
export interface SessionContext {
sessionId: string;
turns: CodegenTurn[];
customInstructions: CustomInstruction[];
customAgents: CustomAgentDefinition[];
userContext: UserContext;
prettierConfig: Record<string, unknown> | null;
state: GenerateCompletionState;
title: string | undefined;
beforeCommit: GitSnapshot | undefined;
createdUnixTime: number;
updatedUnixTime: number;
canLoadMore: boolean;
sessionMode: SessionMode;
}
export interface CodeGenSessionOptionsBase {
sys: DevToolsSys;
credentials: Credentials;
position: CodeGenPosition;
maxTokens?: number;
queueMode?: QueueMode;
/**
* Default {@link SyncChangesFromRemote} policy applied to user messages
* that arrive without an explicit `syncChanges` field. Typically set when
* spawning a sub-agent whose definition declares `defaultSyncChanges`
* (e.g. the setup-project agent uses this to reset to base before each
* message). `undefined` keeps the historical no-op behavior.
*/
defaultSyncChanges?: SyncChangesFromRemote;
privacyMode?: PrivacyMode;
softContextWindow?: number;
builtInCustomInstructions?: CustomInstruction[];
builtInCustomAgents?: SubAgent[];
builtInMCPServerConfig?: MCPServerConfig;
autoImportLocalMCPs?: boolean;
systemPromptOverride?: string | string[];
fusionConfig?: FusionConfig;
devServerOrchestrator?: DevServerOrchestrator;
git?: boolean;
gitAutoInit?: boolean;
workingDirectory?: string;
mcpServers?: boolean;
enabledTools?: (keyof CodeGenToolMap)[];
modelOverride?: string;
agentModelOverrides?: AgentModelOverrides;
skipFileDiff?: boolean;
agentType?: string;
includeMemories?: boolean;
fusionStatusMonitor?: FusionStatusMonitor;
asyncSubAgents?: boolean;
persistSessionLocally?: boolean;
/** Default reasoning effort level for this session. Sent to the service with each completion request. */
reasoning?: ReasoningEffort;
/** Immediate parent session id when this is a sub-agent. */
parentSessionId?: string;
/** Root session of the agent tree. Inherited by every descendant; cost rolls up here in one hop. */
mainSessionId?: string;
}
export interface CodeGenSessionOptionsSession extends CodeGenSessionOptionsBase {
sessionOrCompletionId?: string;
}
export interface CodeGenSessionOptionsInitialUrl extends CodeGenSessionOptionsBase {
initialUrl: string;
}
export type CodeGenSessionOptions = CodeGenSessionOptionsSession | CodeGenSessionOptionsInitialUrl;
export type SpawnAgentResult = SpawnAgentResultSuccess | SpawnAgentResultFailed;
export interface SpawnAgentResultSuccess {
success: boolean;
response: string;
metadata: ExplorationMetadataToolInput;
lastTurn?: CodegenTurn;
sessionId: string;
}
export interface SpawnAgentResultFailed {
success: false;
response: string;
}
export type CodeGenEventEmitter = EventEmitter<{
step: [GenerateCompletionStep];
idle: [];
}>;
export declare class CodeGenSession {
#private;
/**
* Returns the primary git working directory path.
* This is always #gitRepoContexts[0].path for backwards compatibility.
*/
get primaryGitPath(): string;
constructor(options: CodeGenSessionOptions);
get fusionConfig(): FusionConfig | undefined;
/** Returns the first git-enabled folder for backwards compatibility */
get gitEnabledFolder(): WorkspaceFolder | undefined;
/** Returns all git-enabled folders */
get gitEnabledFolders(): WorkspaceFolder[];
/** Returns runtime git repo contexts for all enabled repos */
get gitRepoContexts(): readonly GitRepoContext[];
get workingDirectory(): string;
get bashWorkingDirectory(): string;
getSessionMode(): SessionMode;
switchSessionMode(newMode: SessionMode): Promise<void>;
switchModelOverride(model: string | undefined): Promise<void>;
removeSystemReminder(tag: string): void;
/**
* Returns memories whose glob matches the given file path, excluding ones
* already injected this session. Marks returned memories as reminded.
*/
getMemoriesForFile(filePath: string): MemorySummary[];
queueSystemReminder(reminder: SystemReminderObj): {
operation: string;
};
setDefaultAutoPush(autoPush: AutoPushMode | undefined): void;
setPrivacyMode(privacyMode: PrivacyMode | undefined): Promise<void>;
/**
* Snapshot of the current local MCP statuses (CLI-managed servers only).
* Server-managed MCPs are returned by the codegen pipeline via the
* `mcp-status` step; the slash command merges both maps client-side.
*/
getLocalMcpStatus(): Record<string, MCPClientStatus>;
/**
* Re-emit the merged MCP status snapshot on demand. Lets a freshly-attached
* UI (e.g. the dev-server console connecting before any completion has run)
* pull the current view without firing a noop completion. Server-managed
* entries are whatever was last seen from the codegen pipeline; local
* entries are always live.
*/
refreshMcpStatus(): Record<string, MCPClientStatus>;
/**
* Kick off OAuth for a single CLI-managed MCP. Returns immediately; the
* loopback browser flow completes in the background and emits a
* `local-mcp-auth-complete` step on success/failure (same path the LLM
* `__authenticate` synthetic tool uses).
*
* For server-managed MCPs (`source === "server"`), tells the caller that
* v1 only supports re-auth from the Builder.io org settings — wiring up
* `POST /mcp/oauth/connect` from the CLI is deferred to v2.
*/
authenticateMcpServer(serverName: string): Promise<{
started: boolean;
message?: string;
}>;
/**
* Drop persisted tokens for a CLI-managed remote MCP and surface it as
* `auth_required` so the UI can prompt for re-authentication. Server-
* managed MCPs are out of scope in v1 — token storage is in Firestore
* and requires an authenticated `DELETE /mcp/oauth/credentials` endpoint
* the service doesn't expose yet.
*/
clearMcpAuth(serverName: string): Promise<{
cleared: boolean;
message?: string;
}>;
/**
* Re-run `connectRemoteMCP` for a single CLI-managed server. Useful after
* editing `mcp.json`, clearing auth, or recovering from a transient
* network failure. Server-managed MCPs reconnect fresh on every codegen
* turn and have no long-lived client to bounce — `/mcp` hides the action
* for them.
*/
reconnectMcpServer(serverName: string): Promise<{
success: boolean;
toolsAdded: number;
message?: string;
}>;
/**
* Persist `disabled: true|false` to the right `mcp.json` and refresh the
* status snapshot. The next manager rebuild reads the flag and either
* skips the transport or reconnects; for an immediate refresh we also
* mutate the in-memory client (disable only — re-enabling requires a
* full reconnect on the next turn so transport state is sound).
*/
setMcpServerDisabled(serverName: string, disabled: boolean, scope?: McpScope): Promise<{
disabled: boolean;
path: string;
scope: McpScope;
}>;
initializeSession(opts?: {
skipSessionLoading?: boolean;
signal?: AbortSignal;
}): Promise<void>;
loadHistory(): Promise<LoadHistoryResult>;
loadWholeSession(opts?: LoadWholeSessionOptions): Promise<LoadWholeSessionResult>;
loadMoreTurns(): Promise<CodegenTurn[]>;
setCustomInstructions(instructions: CustomInstruction[]): Promise<void>;
setCustomAgents(agents: SubAgent[]): Promise<void>;
pushRepoV2(repoInfo: {
repoFullName: string;
repoUrl: string;
}): Promise<CodegenApiResult>;
/**
* Ensures git authentication is valid by refreshing git URLs and tokens.
* This should be called before executing git/gh commands to prevent
* authentication failures due to expired tokens.
*/
ensureGitAuth(): Promise<CodegenApiResult>;
zipFolder(folderName: string): Promise<string>;
archiveProject(): Promise<string>;
isIdle(): boolean;
explainBusy(): {
isBusy: boolean;
gitRunning: boolean;
sessionRunning: boolean;
pendingPromises: boolean;
runningSubAgents: boolean;
};
needsBackup(): Promise<boolean>;
uploadBackup(forcedFullBackup?: boolean): Promise<BackupGitRepoResult | {
success: false;
error: Error;
}>;
getCommitMode(): import("#ai-utils").CommitMode;
pushChanges(opts: PushChangesArgs): Promise<CodegenApiResult>;
abortMerge(emitStatus?: boolean): Promise<CodegenApiResult>;
syncChangesFromRemote(opts?: SyncChangesFromRemote): Promise<CodegenApiResult>;
/**
* Get the current commit hash
*/
getCurrentCommitHash(branchName?: string, repoPath?: string): Promise<string | undefined>;
getCurrentBranch(repoPath?: string): Promise<string>;
/**
* Get the feature branch name
*/
getFeatureBranch(): string;
/**
* Get the AI branch name
*/
getAiBranch(): string;
git(args: string[], opts?: string | Partial<RunCommandOptions>): Promise<string>;
setDebug(debug: boolean): void;
createTerminal(options?: CodegenApiCreateTerminal): Promise<CodegenApiTerminal>;
updateTerminal({ terminalId, cols, rows, title }: {
terminalId: string;
cols?: number;
rows?: number;
title?: string;
}): boolean;
writeTerminal({ terminalId, data }: {
terminalId: string;
data: string;
}): boolean;
signalTerminal({ terminalId, signal }: {
terminalId: string;
signal: "SIGINT" | "SIGTERM" | "SIGKILL";
}): boolean;
disposeTerminal({ terminalId, emitTerminals }: {
terminalId: string;
emitTerminals?: boolean;
}): boolean;
restartTerminal({ terminalId }: {
terminalId: string;
}): Promise<boolean>;
subscribeTerminal({ terminalId, onData, onExit }: {
terminalId: string;
onData: (chunk: string) => void;
onExit?: (code?: number) => void;
}): (() => void) | undefined;
getAllFiles(options?: {
getDotFiles?: boolean;
globbyPattern?: string;
includePattern?: string;
gitignore?: boolean;
deep?: number;
truncate?: number;
maxFiles?: number;
onlyFiles?: boolean;
}): Promise<string[]>;
searchFiles(options: SearchFilesOptions): Promise<SearchFilesResult>;
/**
* Search for files by their names/paths using Fuse.js fuzzy matching.
* Matches against the full path (including directory segments), so
* "components" matches "src/components/Button.tsx" — unlike the old
* ripgrep glob approach which only matched the filename.
* Respects access control policies (deny patterns).
*/
searchFileTree(options: SearchFileTreeOptions): Promise<SearchFileTreeResult>;
collectRepoMetrics(opts?: {
rootPath?: string;
folderName?: string;
}): Promise<RepoMetrics>;
getSessionId(): string;
/** Root session id passed to each spawned sub-agent as their mainSessionId. */
getMainSessionId(): string;
updateMetadata(metadata: Record<string, any>): void;
getMetadata(): Record<string, any>;
setTimeline(timeline: TimelineCollector): void;
getTimeline(): TimelineCollector | null;
getSpaceId(): string | undefined;
revertToCommitSnapshot(commitSnapshot: GitSnapshot): Promise<void>;
revertToCommitHash(commitHash: string): Promise<void>;
resetToCommitSnapshot(commitSnapshot: GitSnapshot, requestRefresh?: boolean): Promise<void>;
resetToCommitHash(commitHash: string, requestRefresh?: boolean): Promise<void>;
/**
* Core function to restore the codebase to a state that matches a predicate.
* This is the main function that handles both git-based and file-based restoration.
*
* @param predicate Function that takes a turn and its index and returns true if we should restore up to that turn
* @param dryRun If true, only simulate the restoration without making changes
* @returns Array of file paths that were changed
*/
restore({ location, predicate, revert, dryRun, forceReplay, debug }: {
location: "before" | "after";
predicate: (turn: CodegenTurn | null, index: number) => boolean;
dryRun?: boolean;
revert?: boolean;
forceReplay?: boolean;
debug?: string;
}): Promise<{
undone: string[] | null;
message: string;
}>;
restoreFromCompletionId({ location, completionId, forceReplay }: {
location: "before" | "after";
completionId: string;
forceReplay?: boolean;
}): Promise<{
undone: string[] | null;
message: string;
}>;
restoreBeforeCompletionId(completionId: string): Promise<{
undone: string[] | null;
message: string;
}>;
/**
* Undo all changes back to the last user message
*/
undoLastUserMessage(dryRun?: boolean): Promise<{
undone: string[] | null;
message: string;
}>;
getLastUserCompletionId(): string | undefined;
getLastCompletionId(): string | undefined;
getCurrentState(): GenerateCompletionState;
getLastApplyResultsTurn(): CodegenTurn | undefined;
getLastTurn(): CodegenTurn | undefined;
getLastMessage(): string | undefined;
getNextUrl(): string | undefined;
sendFeedback(feedback: Partial<CodegenFeedback>): Promise<void>;
lastTurnHasChanges(): Promise<boolean>;
waitUntilState(state: GenerateCompletionState, timeout?: number): Promise<void>;
getProxyConfig(): {
proxyOrigin: string | undefined;
proxyDefaultOrigin: string | undefined;
proxyDestination: string | undefined;
};
clearSession(): Promise<void>;
clearMessageQueue(emitUserMessages?: boolean): Promise<void>;
updateMessage(opts: MessageUpdateOptions): void;
flushMessageQueue(): Promise<void>;
sendMessage(message: GenerateUserMessage | GenerateUserMessage[]): Promise<string | null>;
/**
* Waits for the setup command to finish if it's currently running.
* Returns information about the setup command state.
*
* @param signal - Optional abort signal
* @returns Object with setup command state information
*/
waitForSetupCommand(signal?: AbortSignal): Promise<{
state: "installed" | "not-installed" | "install-failed" | "install-aborted";
shouldProceed: boolean;
}>;
waitForDevServer(signal?: AbortSignal, timeoutMs?: number, expectedStates?: HttpServerState[]): Promise<{
shouldProceed: boolean;
message: string;
serverUrl?: string;
}>;
getNamedAgentConfig(agentName: string | undefined, sessionId: string, signal?: AbortSignal): Promise<{
success: true;
options: CodeGenSessionOptions;
resetAfterRun: boolean;
subagent_type: string;
/** Set when the run was launched with browser session replay enabled. */
replayId?: string;
/**
* HTTP(S) URL to force-close the browser-service WebSocket on
* agent shutdown/abort. Present whenever a Chrome connection
* was acquired (not just when replay is enabled) so the pool
* slot is freed promptly instead of sitting idle for 30min.
*/
browserDisconnectUrl?: string;
/** Optional override for the maximum timeout for this agent. */
maxTimeoutMs?: number;
} | {
success: false;
response: string;
}>;
/**
* Spawn a named custom agent by ID or name
* @param agentNameOrId - The agent's name or ID
* @param options - Additional spawning options
* @returns SpawnAgentResult
*/
spawnNamedAgent(agentName: string | undefined, options: {
prompt: string;
user: UserSource;
signal: AbortSignal;
maxCompletions: number;
sessionId: string;
resume?: string;
mcpServers?: boolean;
attachmentUrls?: string[];
}): Promise<SpawnAgentResult>;
/**
* Returns info about currently running background sub-agents.
*/
getRunningSubAgents(): Array<{
id: string;
subagentType: string;
description: string;
startedAt: number;
resumed: boolean;
turns: number;
state: GenerateCompletionState | "unknown";
toolsRunning: boolean;
}>;
canHandleTool(toolName: keyof CodeGenToolMap): boolean;
/**
* Declare tools that are handleable at the session level, independent of
* live streaming connections. Used by fire-and-forget (queue-based)
* integrations like Slack where tool calls are handled asynchronously
* via event hooks rather than a persistent connection.
*
* These capabilities are automatically cleared when the session goes idle,
* simulating a "disconnect". The next message from the integration will
* re-register them.
*/
setSessionCanHandleTools(tools: (keyof CodeGenToolMap)[]): void;
clearSessionCanHandleTools(): void;
setProxyOrigin(proxySrc: string | undefined, proxyDefaultOrigin: string | undefined): void;
getTurns(): CodegenTurn[];
getSessionContext(): SessionContext;
runSetupCommand(): Promise<import("#ai-utils").SetupCommandResult | undefined>;
abortSetupCommand(): Promise<import("#ai-utils").SetupCommandResult | undefined> | undefined;
abortValidateCommand(): Promise<import("#ai-utils").ValidateCommandResult | undefined> | undefined;
toolsRunning(): boolean;
abortAllTools(): void;
/**
* Fulfil a pending tool call (usually AskUser or any passThrough tool)
* Exposed via websocket as `toolFullfilment` for the Builder UI to send back
* the user's response.
*/
toolFullfilment(id: string, result: ToolResolution | string): boolean;
cancelAllPendingToolFulfilments(userMessage?: string): void;
fulfillToolCall(id: string, result: ToolResolution): boolean;
abortToolCall(id: string, reason?: string): boolean;
acceptCode(): Promise<void>;
abort(abortOptions?: CodegenAbortOptions | boolean): Promise<boolean>;
stopEventLoop(): Promise<void>;
requestRefresh(): void;
configureDevOrchestrator(opts: ConfigureDevOrchestratorOpts): Promise<ConfigureDevOrchestratorUpdates>;
/**
* Subscribe to file change events for the entire working directory.
* Returns a dispose function to unsubscribe.
* The watcher is lazily initialized on first subscription and cleaned up on last unsubscribe.
*/
subscribeToFileChanges(onEvent: (event: FolderWatchEvent) => void): () => Promise<void>;
tsServerInit(): Promise<{
ok: boolean;
cwd?: string;
error?: string;
}>;
tsServerOpenFile(filePath: string, content: string): Promise<void>;
tsServerChangeFile(filePath: string, content: string): Promise<void>;
tsServerCloseFile(filePath: string): Promise<void>;
tsServerGetCompletions(filePath: string, line: number, offset: number, options?: {
triggerCharacter?: string;
includeExternalModuleExports?: boolean;
}): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetCompletionDetails(filePath: string, line: number, offset: number, entryNames: string[]): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetDefinition(filePath: string, line: number, offset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetTypeDefinition(filePath: string, line: number, offset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetQuickInfo(filePath: string, line: number, offset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetReferences(filePath: string, line: number, offset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetSignatureHelp(filePath: string, line: number, offset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerRequestDiagnostics(filePaths: string[]): Promise<void>;
tsServerGetRenameLocations(filePath: string, line: number, offset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetNavTree(filePath: string): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerOrganizeImports(filePath: string): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetInlayHints(filePath: string): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetCodeFixes(filePath: string, startLine: number, startOffset: number, endLine: number, endOffset: number, errorCodes: number[]): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetApplicableRefactors(filePath: string, startLine: number, startOffset: number, endLine: number, endOffset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetEditsForRefactor(filePath: string, startLine: number, startOffset: number, endLine: number, endOffset: number, refactorName: string, actionName: string): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetImplementation(filePath: string, line: number, offset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetSelectionRange(filePath: string, locations: Array<{
line: number;
offset: number;
}>): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetLinkedEditingRange(filePath: string, line: number, offset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
tsServerGetFormatRange(filePath: string, startLine: number, startOffset: number, endLine: number, endOffset: number): Promise<import("./tsserver-manager").TsServerRequestResult>;
subscribeToDiagnostics(onEvent: (event: any) => void): () => void;
close(opts?: {
reason?: string;
uploadGitBackup?: boolean;
}): Promise<void>;
sendMessageToOrgAgent(options: SendMessageToOrgAgentInput): Promise<{
status: "success";
} | {
status: "error";
error: string;
}>;
/**
* Queues a semantic git status reminder for the LLM.
* Call this after significant git operations to keep the LLM informed.
*/
queueGitStatusReminder(context: {
trigger: "session-start" | "sync-success" | "push-success" | "pull-success";
syncedBranches?: string[];
pushedToRemote?: boolean;
}): void;
manualCommit(options: {
add: string;
commitMessage: string;
folderName?: string;
}): Promise<boolean>;
getLastSuggestedAction(): SuggestedActionBuildError | undefined;
connectToEventLoop(shouldReplay: boolean, onStep: (step: GenerateCompletionStep) => void, options?: {
canHandleTools?: (keyof CodeGenToolMap)[];
}): () => void;
waitUntilPendingPromises(signal?: AbortSignal): Promise<void>;
waitUntilBlockingPendingPromises(signal?: AbortSignal): Promise<void>;
waitUntilIdle(signal?: AbortSignal): Promise<void>;
waitForEventLoop(): Promise<void>;
/**
* Stages and optionally commits work in progress.
*
* When `#batchCommitsEnabled` is false (default): stages AND commits immediately.
* When `#batchCommitsEnabled` is true: only stages files, sets #pendingCommit flag.
*
* @returns commit hash if committed, false if no changes or staged only
*/
commitWorkInProgress(defaultCommitMessage: string, changedFiles: string[]): Promise<void>;
getChangesReport(): Promise<{
diff: string;
files: string[];
} | undefined>;
/**
* Resolves a workspace file path to its actual file system path
* @param filePath A file path that may include a workspace prefix (e.g., "workspace1/path/to/file.js")
* @param forceWorkspace If true, will try the first workspace as fallback when no workspace folder is found
* @returns The actual file system path and the workspace folder it belongs to
*/
resolveWorkspacePath(filePath: string, forceWorkspace: boolean): {
resolvedPath: string;
workspaceFolder?: WorkspaceFolder;
};
/**
* Converts an absolute file system path to a workspace URL
* @param absolutePath The absolute file system path to convert
* @returns The workspace URL if the path can be converted, undefined otherwise
*/
absolutePathToWorkspaceUrl(absolutePath: string): string | undefined;
/**
* Launches the editor for a given file path
* @param filePath The file path to launch the editor for
* @param line The line number to launch the editor at
* @param column The column number to launch the editor at
* @returns void
*/
launchEditor(opts?: {
filePath?: string;
line?: number;
column?: number;
}): Promise<{
success: boolean;
message?: string;
}>;
/**
* Reads a file from the workspace
* @param filePath A file path that may include a workspace prefix
* @returns The file content or null if the file doesn't exist
*/
readFile(filePath: string, skipAclCheck?: boolean): Promise<string | null>;
/**
* Reads a file and returns content with a checksum for caching.
* If previousChecksum is provided and matches, returns notModified: true
* without sending the file content.
*/
readFileWithChecksum(args: {
filePath: string;
previousChecksum?: string;
skipAclCheck?: boolean;
}): Promise<{
content: string | null;
checksum: string | null;
notModified?: boolean;
}>;
/**
* Reads a binary file from the workspace and returns its content as base64.
* Unlike readFile which reads as UTF-8 (corrupting binary data), this method
* preserves binary content by encoding it as base64.
*/
readBinaryFile(filePath: string): Promise<string | null>;
/**
* Gets file content at a specific git reference (e.g., origin/main, HEAD~1, commit hash)
* @param filePath - The file path relative to the workspace
* @param gitRef - The git reference (branch, commit, tag) - defaults to parent branch
* @returns The file content at that reference, or null if not found
*/
getFileAtRef(filePath: string, gitRef?: string, repoPathOverride?: string): Promise<string | null>;
/**
* Gets the diff information for a single file including full content
* @param args - Either an options object or a file path string (legacy)
* @param opts.path - The file path, can be folder-prefixed (e.g., 'builder-anime-works/client/App.tsx') or relative
* @param opts.folderName - Optional folder name (deprecated, prefer folder-prefixed path)
* @param opts.mode - Diff baseline mode. "remote-parent-branch" (default)
* compares against the parent branch merge-base. "remote-current-branch"
* compares against origin/<currentBranch> (with fallback to merge-base
* when the branch has never been pushed).
* @returns Object with oldContent (from baseline ref) and newContent (current)
*/
getSingleFileDiff(args: {
path: string;
folderName?: string;
previousChecksum?: string;
mode?: "remote-parent-branch" | "remote-current-branch";
} | string): Promise<{
oldContent: string | null;
newContent: string | null;
action: "create" | "update" | "delete";
checksum: string | null;
notModified?: boolean;
}>;
/**
* Discards changes for a specific file by creating a revert commit
* @param args - Either an options object or a file path string (legacy)
* @param opts.filePath - The file path, can be folder-prefixed (e.g., 'builder-anime-works/client/App.tsx')
* @param opts.folderName - Optional folder name (deprecated, prefer folder-prefixed path)
* @returns success status, commit hash for reverting, and optional error message
*/
discardFileChanges(args: {
filePath: string;
folderName?: string;
} | string): Promise<{
success: boolean;
commitHash?: string;
error?: string;
}>;
/**
* Reverts a discard commit using git revert
* @param commitHash - The commit hash to revert
* @param folderName - Optional folder name to target specific repo (more efficient)
* @returns success status and optional error message
*/
revertDiscard(options: {
commitHash: string;
folderName?: string;
}): Promise<{
success: boolean;
error?: string;
}>;
/**
* Checks if a file exists in the workspace
* @param filePath A file path that may include a workspace prefix
* @returns True if the file exists, false otherwise
*/
fileExists(filePath: string): Promise<{
absolutePath: string | undefined;
recommendedPath: string | undefined;
workspaceFolder: WorkspaceFolder | undefined;
virtual: boolean;
}>;
/**
* Reads a file from the workspace synchronously
* @param filePath A file path that may include a workspace prefix
* @returns The file content or null if the file doesn't exist
*/
readFileSync(filePath: string, skipAclCheck?: boolean): string | null;
/**
* Writes content to a file in the workspace
* @param filePath A file path that may include a workspace prefix
* @param content The content to write
* @returns True if the write was successful, false otherwise
*/
writeFile(filePath: string, content: string | Uint8Array, skipAclCheck?: boolean): Promise<string | null>;
/**
* Lists files in a directory in the workspace
* @param dirPath A directory path that may include a workspace prefix
* @returns Array of file names in the directory or empty array if directory doesn't exist
*/
listDir(dirPath: string): Promise<string[]>;
/**
* List custom instructions (rules and skills) discovered from the workspace.
* Returns instructions with isSkill flag to distinguish skills from rules.
*/
listCustomInstructions(): Promise<Array<{
id: string;
name: string;
description?: string;
type?: string;
isSkill: boolean;
disableModelInvocation?: boolean;
userInvocable?: boolean;
}>>;
/**
* Get stats for a file in the workspace
* @param filePath A file path that may include a workspace prefix
* @returns The file stats or null if the file doesn't exist
*/
stat(filePath: string, skipAclCheck?: boolean): Promise<{
isDirectory: () => boolean;
isFile: () => boolean;
size: number;
} | null>;
/**
* Deletes a file or directory from the workspace.
* Directories are removed recursively along with all of their contents.
* @param filePath A file or directory path that may include a workspace prefix
* @returns The resolved path that was deleted, or null on failure / if it didn't exist
*/
deleteFile(filePath: string, skipAclCheck?: boolean): Promise<string | null>;
getLinesStats(): {
added: number;
removed: number;
net: number;
};
/**
* Get git diff between current commit and remote branch
* If remote current branch doesn't exist, gets diff between default branch and current branch
* @param numberOfContextLines - Optional number of context lines to include in the diff
* @param includeFilesOnly - If true, only return filePath and action properties
* @param filePaths - Optional array of file paths to limit the diff to specific files
* @param folderName - Optional folder name to specify which repo to get diff from
*/
getDiffFromRemote({ numberOfContextLines, includeFilesOnly, filePaths, folderName }: {
numberOfContextLines?: number;
includeFilesOnly?: boolean;
filePaths?: Array<string>;
folderName?: string;
}): Promise<ApplyActionsResult[]>;
/**
* Get git diff based on the specified mode
* @param mode - The diff mode: 'previous-commit', 'parent-branch', or 'remote'
* @param numberOfContextLines - Optional number of context lines to include in the diff (e.g., 999 for -U999)
* @param includeFilesOnly - If true, only return filePath and action properties
* @param filePaths - Optional array of file paths to limit the diff to specific files
* @param folderName - Optional folder name to specify which repo to get diff from
*/
getDiff({ mode, numberOfContextLines, includeFilesOnly, filePaths, folderName }: {
mode: "remote-parent-branch" | "remote-current-branch";
numberOfContextLines?: number;
includeFilesOnly?: boolean;
filePaths?: Array<string>;
folderName?: string;
}): Promise<{
state: "error" | "success";
diff?: ApplyActionsResult[];
error?: Error;
}>;
private getDefaultBranch;
private getDiffFromParentBranch;
}