Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ import {
SetMcpToolsEnabledRequest,
McpLoadErrorsDTO,
AgentsMdFileInfoDTO,
SelectContextFilesRequest,
Attachment,
} from "./interfaces";

export interface AIPanelAPI {
Expand Down Expand Up @@ -185,4 +187,8 @@ export interface AIPanelAPI {
getMcpLoadErrors: () => Promise<McpLoadErrorsDTO>;
getAgentsMdFileInfo: () => Promise<AgentsMdFileInfoDTO>;
openOrCreateAgentsMd: () => Promise<void>;
// ==================================
// File Attachment via VS Code Dialog
// ==================================
selectContextFiles: (params: SelectContextFilesRequest) => Promise<Attachment[]>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ export interface ExistingFunction {
// ==================================
// Attachment-Related Interfaces
// ==================================
export interface SelectContextFilesRequest {
command: Command | null;
skillCommand?: SkillCommand;
}

export interface Attachment {
name: string;
path?: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ import {
AgentsMdFileInfoDTO,
ParseSkillFileRequest,
ParseSkillFileResponse,
SelectContextFilesRequest,
Attachment,
} from "./interfaces";
import { RequestType, NotificationType } from "vscode-messenger-common";

Expand Down Expand Up @@ -162,3 +164,4 @@ export const mcpLoadErrorsChanged: NotificationType<McpLoadErrorsDTO> = { method
export const getAgentsMdFileInfo: RequestType<void, AgentsMdFileInfoDTO> = { method: `${_preFix}/getAgentsMdFileInfo` };
export const openOrCreateAgentsMd: RequestType<void, void> = { method: `${_preFix}/openOrCreateAgentsMd` };
export const agentsMdFileInfoChanged: NotificationType<AgentsMdFileInfoDTO> = { method: `${_preFix}/agentsMdFileInfoChanged` };
export const selectContextFiles: RequestType<SelectContextFilesRequest, Attachment[]> = { method: `${_preFix}/selectContextFiles` };
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ import {
OpenMcpConfigRequest,
getAgentsMdFileInfo,
openOrCreateAgentsMd,
selectContextFiles,
SelectContextFilesRequest,
} from "@wso2/ballerina-core";
import { workspace } from 'vscode';
import { Messenger } from "vscode-messenger";
Expand Down Expand Up @@ -233,6 +235,7 @@ export function registerAiPanelRpcHandlers(messenger: Messenger) {
messenger.onRequest(getMcpLoadErrors, () => rpcManger.getMcpLoadErrors());
messenger.onRequest(getAgentsMdFileInfo, () => rpcManger.getAgentsMdFileInfo());
messenger.onRequest(openOrCreateAgentsMd, () => rpcManger.openOrCreateAgentsMd());
messenger.onRequest(selectContextFiles, (args: SelectContextFilesRequest) => rpcManger.selectContextFiles(args));

// Push updates to the webview whenever the set of running services changes.
runningServicesManager.onChange = (services) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ import {
AIPanelPrompt,
AbortAIGenerationRequest,
AddFilesToProjectRequest,
Attachment,
AttachmentStatus,
CheckpointInfo,
Command,
SkillCommand,
DocGenerationRequest,
GenerateAgentCodeRequest,
GenerateOpenAPIRequest,
Expand All @@ -40,6 +43,7 @@ import {
RestoreCheckpointRequest,
SemanticDiffRequest,
SemanticDiffResponse,
SelectContextFilesRequest,
SubmitFeedbackRequest,
TestGenerationMentions,
UIChatMessage,
Expand Down Expand Up @@ -1273,4 +1277,40 @@ User reverted the last made changes. The files have been restored to the state b
}
}

async selectContextFiles(params: SelectContextFilesRequest): Promise<Attachment[]> {
const projectPath = StateMachine.context().projectPath;
const defaultUri = projectPath ? vscode.Uri.file(projectPath) : vscode.Uri.file(os.homedir());
Comment on lines +1281 to +1282

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use resolved project root for the picker default directory.

Line 1281 uses only StateMachine.context().projectPath. In workspace-root flows, workspacePath can be the active root, so this can still open outside the current project (the issue this PR is fixing).

Suggested fix
-        const projectPath = StateMachine.context().projectPath;
-        const defaultUri = projectPath ? vscode.Uri.file(projectPath) : vscode.Uri.file(os.homedir());
+        const projectRootPath = resolveProjectRootPath();
+        const defaultUri = projectRootPath ? vscode.Uri.file(projectRootPath) : vscode.Uri.file(os.homedir());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const projectPath = StateMachine.context().projectPath;
const defaultUri = projectPath ? vscode.Uri.file(projectPath) : vscode.Uri.file(os.homedir());
const projectRootPath = resolveProjectRootPath();
const defaultUri = projectRootPath ? vscode.Uri.file(projectRootPath) : vscode.Uri.file(os.homedir());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts`
around lines 1281 - 1282, The picker default directory is still derived only
from StateMachine.context().projectPath, which can miss the resolved workspace
root in workspace-root flows and open outside the current project. Update the
default URI logic in the rpc-manager.ts picker setup to use the resolved project
root (prefer workspacePath when available, falling back to projectPath, then
home directory) so the picker always starts inside the active workspace. Keep
the change localized around the projectPath/defaultUri assignment in
rpc-manager.ts.


const uris = await vscode.window.showOpenDialog({
canSelectMany: true,
canSelectFiles: true,
canSelectFolders: false,
defaultUri,
openLabel: "Attach",
});

if (!uris || uris.length === 0) {
return [];
}

const MAX_FILE_SIZE = 5 * 1024 * 1024;
const isDataMapper = params.skillCommand === SkillCommand.DataMap || params.command === Command.TypeCreator;

return Promise.all(uris.map(async (uri) => {
const filePath = uri.fsPath;
const name = path.basename(filePath);
try {
const stat = await fs.promises.stat(filePath);
if (stat.size > MAX_FILE_SIZE) {
return { name, status: AttachmentStatus.FileSizeExceeded };
}
const content = isDataMapper
? (await fs.promises.readFile(filePath)).toString("base64")
: await fs.promises.readFile(filePath, "utf-8");
return { name, content, status: AttachmentStatus.Success };
} catch {
return { name, status: AttachmentStatus.UnknownError };
}
}));
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ import {
SetMcpToolsEnabledRequest,
McpLoadErrorsDTO,
AgentsMdFileInfoDTO,
selectContextFiles,
SelectContextFilesRequest,
Attachment,
} from "@wso2/ballerina-core";
import { HOST_EXTENSION } from "vscode-messenger-common";
import { Messenger } from "vscode-messenger-webview";
Expand Down Expand Up @@ -479,4 +482,8 @@ export class AiPanelRpcClient implements AIPanelAPI {
openOrCreateAgentsMd(): Promise<void> {
return this._messenger.sendRequest(openOrCreateAgentsMd, HOST_EXTENSION);
}

selectContextFiles(params: SelectContextFilesRequest): Promise<Attachment[]> {
return this._messenger.sendRequest(selectContextFiles, HOST_EXTENSION, params);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2283,6 +2283,8 @@ const AIChat: React.FC = () => {
multiple: true,
acceptResolver: acceptResolver,
handleAttachmentSelection: handleAttachmentSelection,
onAttachClick: (command, skillCommand) =>
rpcClient.getAiPanelRpcClient().selectContextFiles({ command, skillCommand }),
}}
suggestedCommandTemplates={footerSuggestedCommandTemplates}
inputPlaceholder={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
*/

import { useState, useRef, ChangeEvent } from "react";
import { Attachment, Command, SkillCommand } from "@wso2/ballerina-core";
import { Attachment, AttachmentStatus, Command, SkillCommand } from "@wso2/ballerina-core";

export interface AttachmentOptions {
multiple: boolean;
acceptResolver: (command: Command | null, skillCommand?: SkillCommand) => string;
handleAttachmentSelection: (e: ChangeEvent<HTMLInputElement>, command: Command | null, skillCommand?: SkillCommand) => Promise<Attachment[]>;
onAttachClick?: (command: Command | null, skillCommand?: SkillCommand) => Promise<Attachment[]>;
}

interface UseAttachmentsProps {
Expand All @@ -35,9 +36,30 @@ export function useAttachments({ attachmentOptions, activeCommand, activeSkillCo
const [attachments, setAttachments] = useState<Attachment[]>([]);
const fileInputRef = useRef<HTMLInputElement | null>(null);

// open file input
function handleAttachClick() {
if (fileInputRef.current) {
// open file picker
async function handleAttachClick() {
if (attachmentOptions.onAttachClick) {
try {
const results = await attachmentOptions.onAttachClick(activeCommand, activeSkillCommand);
setAttachments((prev) => {
const updated = [...prev];
results
.filter((newFile) => newFile.status === AttachmentStatus.Success)
.forEach((newFile) => {
const existingIndex = updated.findIndex(
(existing) => existing.name === newFile.name && existing.content === newFile.content
);
if (existingIndex !== -1) {
updated.splice(existingIndex, 1);
}
updated.push(newFile);
});
return updated;
});
} catch (error) {
console.error("Failed to select context files", error);
}
} else if (fileInputRef.current) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fileInputRef.current.click();
}
}
Expand Down
Loading