diff --git a/.vscodeignore b/.vscodeignore index 53fd3798c01..1bc718e577e 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -29,6 +29,9 @@ jest.* .roomodes .rooignore .roo/** +.pearai-agent/** +.pearai-agent-ignore +.pearai-agent-modes benchmark/** cline_docs/** e2e/** diff --git a/package-lock.json b/package-lock.json index 5abe8b91a57..526bae528ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@google/genai": "^0.9.0", "@mistralai/mistralai": "^1.3.6", "@modelcontextprotocol/sdk": "^1.7.0", + "@pearai/core": "file:./../pearai-submodule/core", "@types/clone-deep": "^4.0.4", "@types/pdf-parse": "^1.1.4", "@types/tmp": "^0.2.6", @@ -112,6 +113,94 @@ "vscode": "^1.84.0" } }, + "../pearai-submodule/core": { + "name": "@continuedev/core", + "version": "1.0.13", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-bedrock-runtime": "^3.620.1", + "@aws-sdk/credential-providers": "^3.620.1", + "@continuedev/config-types": "^1.0.10", + "@continuedev/llm-info": "^1.0.1", + "@mozilla/readability": "^0.5.0", + "@octokit/rest": "^20.0.2", + "@supabase/supabase-js": "^2.45.1", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", + "@xenova/transformers": "2.14.0", + "adf-to-md": "^1.1.0", + "async-mutex": "^0.5.0", + "axios": "^1.6.7", + "cheerio": "^1.0.0-rc.12", + "commander": "^12.0.0", + "comment-json": "^4.2.3", + "dbinfoz": "^0.1.4", + "dotenv": "^16.4.5", + "fastest-levenshtein": "^1.0.16", + "follow-redirects": "^1.15.5", + "handlebars": "^4.7.8", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "ignore": "^5.3.1", + "js-tiktoken": "^1.0.8", + "jsdom": "^24.0.0", + "jwt-decode": "^4.0.0", + "launchdarkly-node-client-sdk": "^3.2.0", + "llm-code-highlighter": "^0.0.14", + "mac-ca": "^3.1.0", + "node-fetch": "^3.3.2", + "node-html-markdown": "^1.3.0", + "ollama": "^0.4.6", + "onnxruntime-node": "1.14.0", + "openai": "^4.20.1", + "pg": "^8.11.3", + "posthog-node": "^3.6.3", + "quick-lru": "^7.0.0", + "replicate": "^0.26.0", + "request": "^2.88.2", + "socket.io-client": "^4.7.3", + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7", + "system-ca": "^1.0.3", + "tree-sitter-wasms": "^0.1.11", + "uuid": "^9.0.1", + "vectordb": "^0.4.20", + "web-tree-sitter": "^0.21.0", + "win-ca": "^3.5.1", + "workerpool": "^9.1.3", + "yaml": "^2.4.2", + "zod": "^3.23.8" + }, + "devDependencies": { + "@babel/preset-env": "^7.24.7", + "@biomejs/biome": "1.6.4", + "@google/generative-ai": "^0.11.4", + "@types/follow-redirects": "^1.14.4", + "@types/jest": "^29.5.12", + "@types/jquery": "^3.5.29", + "@types/jsdom": "^21.1.6", + "@types/mozilla-readability": "^0.2.1", + "@types/mustache": "^4.2.5", + "@types/node-fetch": "^2.6.11", + "@types/pg": "^8.11.6", + "@types/request": "^2.48.12", + "@types/uuid": "^9.0.7", + "@types/win-ca": "^3.5.4", + "cross-env": "^7.0.3", + "esbuild": "^0.17.19", + "eslint": "^8", + "eslint-plugin-import": "^2.29.1", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "myers-diff": "^2.1.0", + "onnxruntime-common": "1.14.0", + "onnxruntime-web": "1.14.0", + "ts-jest": "^29.1.1" + }, + "engines": { + "node": ">=20.11.0" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -6811,6 +6900,10 @@ "dev": true, "license": "MIT" }, + "node_modules/@pearai/core": { + "resolved": "../pearai-submodule/core", + "link": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", diff --git a/package.json b/package.json index 1b372e92267..6b3db19a012 100644 --- a/package.json +++ b/package.json @@ -384,6 +384,7 @@ "@google/genai": "^0.9.0", "@mistralai/mistralai": "^1.3.6", "@modelcontextprotocol/sdk": "^1.7.0", + "@pearai/core": "file:./../pearai-submodule/core", "@types/clone-deep": "^4.0.4", "@types/pdf-parse": "^1.1.4", "@types/tmp": "^0.2.6", diff --git a/src/activate/index.ts b/src/activate/index.ts index 658bf467f7a..2fff46a5f04 100644 --- a/src/activate/index.ts +++ b/src/activate/index.ts @@ -2,3 +2,4 @@ export { handleUri } from "./handleUri" export { registerCommands } from "./registerCommands" export { registerCodeActions } from "./registerCodeActions" export { registerTerminalActions } from "./registerTerminalActions" +export { registerPearListener } from "./registerPearListener" diff --git a/src/activate/registerPearListener.ts b/src/activate/registerPearListener.ts new file mode 100644 index 00000000000..f40be03b73c --- /dev/null +++ b/src/activate/registerPearListener.ts @@ -0,0 +1,115 @@ +import * as vscode from "vscode" +import { ClineProvider } from "../core/webview/ClineProvider" +import { assert } from "../utils/util" +import { PEARAI_CREATOR_MODE_WEBAPP_MANAGER_SLUG } from "../shared/modes" + +export const getPearaiExtension = async () => { + const pearAiExtension = vscode.extensions.getExtension("pearai.pearai") + + assert(!!pearAiExtension, "PearAI extension not found") + + if (!pearAiExtension.isActive) { + await pearAiExtension.activate() + } + + return pearAiExtension +} + +// TODO: TYPES +export const getpearAIExports = async () => { + const pearAiExtension = await getPearaiExtension() + + assert(!!pearAiExtension.exports, "⚠️⚠️ Error, no PearAI Exports could be found :( ⚠️⚠️"); + + return pearAiExtension.exports; +} + +// TODO: SHOULD HAVE TYPE SYNCED WITH THE PEARAI SUBMODULE! +type CreatorModeState = "OVERLAY_CLOSED" | "OVERLAY_OPEN" | "OVERLAY_CLOSED_CREATOR_ACTIVE" + +export const registerPearListener = async (provider: ClineProvider) => { + // Getting the pear ai extension instance + const exports = await getpearAIExports() + + exports.pearAPI.creatorMode.onDidRequestExecutePlan(async (msg: any) => { + console.dir(`onDidRequestNewTask triggered with: ${JSON.stringify(msg)}`) + + let canContinue = false; + + while(!canContinue) { + await new Promise((resolve) => setTimeout(resolve, 10)); + canContinue = provider.viewLaunched && provider.isViewLaunched; + } + + + // Get the sidebar provider + // Focus the sidebar first + await vscode.commands.executeCommand("pearai-roo-cline.SidebarProvider.focus") + + // Wait for the view to be ready using a helper function + await ensureViewIsReady(provider) + // Wait a brief moment for UI to update + await new Promise((resolve) => setTimeout(resolve, 3000)) + + // * This does actually work but the UI update does not happen. This method calls this.postStateToWebview() so not sure what is going on - James + if(msg.newProjectType === "WEBAPP") { + // Only switch to the creator manager if we're creating a new project + // TODO: later when we need to make a different type of project, we need to change this + await provider.handleModeSwitch(PEARAI_CREATOR_MODE_WEBAPP_MANAGER_SLUG); + } + + // Clicl the chat btn + await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" }) + + const creatorModeConfig = { + creatorMode: true, + newProjectType: msg.newProjectType, + newProjectPath: msg.newProjectPath, + } + + + // Initialize with task + await provider.initClineWithTask(msg.plan, undefined, undefined, undefined, creatorModeConfig); + }); + // If there's a creator event in the cache after the extensions were refreshed, we need to get it! + exports.pearAPI.creatorMode.triggerCachedCreatorEvent(true); + + exports.pearAPI.creatorMode.onDidChangeCreatorModeState(async (state: CreatorModeState) => { + // Get the sidebar provider + const sidebarProvider = ClineProvider.getVisibleInstance(); + + if (sidebarProvider) { + // Send a message to the webview that will trigger a window event + sidebarProvider.postMessageToWebview({ + type: "creatorModeUpdate", + text: state, + }); + } + }); + +} + +// TODO: decide if this is needed +// Helper function to ensure the webview is ready +async function ensureViewIsReady(provider: ClineProvider): Promise { + // If the view is already launched, we're good to go + if (provider.viewLaunched) { + return + } + + // Otherwise, we need to wait for it to initialize + return new Promise((resolve) => { + // Set up a one-time listener for when the view is ready + const disposable = provider.on("clineCreated", () => { + // Clean up the listener + disposable.dispose() + resolve() + }) + + // Set a timeout just in case + setTimeout(() => { + disposable.dispose() + resolve() + }, 5000) + }) +} diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index f80fde03439..b65e89e0bb3 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -105,9 +105,18 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa case "claude-3-opus-20240229": case "claude-3-haiku-20240307": betas.push("prompt-caching-2024-07-31") + // Include prompt_key if newProjectType is set return { - headers: { "anthropic-beta": betas.join(",") }, - authorization: `Bearer ${this.options.apiKey}`, + headers: { + "anthropic-beta": betas.join(","), + prompt_key: this.options.creatorModeConfig?.newProjectType + ? String(this.options.creatorModeConfig.newProjectType) + : undefined, + project_path: this.options.creatorModeConfig?.newProjectPath + ? String(this.options.creatorModeConfig.newProjectPath) + : undefined, + authorization: `Bearer ${this.options.apiKey}`, + }, } default: return undefined diff --git a/src/api/providers/pearai/pearai.ts b/src/api/providers/pearai/pearai.ts index 42e170ff566..f84a75c4b03 100644 --- a/src/api/providers/pearai/pearai.ts +++ b/src/api/providers/pearai/pearai.ts @@ -116,28 +116,33 @@ export class PearAIHandler extends BaseProvider implements SingleCompletionHandl } async *createMessage(systemPrompt: string, messages: any[]): AsyncGenerator { - const generator = this.handler.createMessage(systemPrompt, messages) - let warningMsg = "" + try { + const generator = this.handler.createMessage(systemPrompt, messages) + let warningMsg = "" - for await (const chunk of generator) { - console.dir(chunk) - if (chunk.type === "text" && chunk.metadata?.ui_only) { - warningMsg += chunk.metadata?.content - continue + for await (const chunk of generator) { + console.dir(chunk) + if (chunk.type === "text" && chunk.metadata?.ui_only) { + warningMsg += chunk.metadata?.content ?? "" + continue + } + yield chunk } - yield chunk - } - if (warningMsg) { - if (warningMsg.includes("pay-as-you-go")) { - vscode.window.showInformationMessage(warningMsg, "View Pay-As-You-Go").then((selection) => { - if (selection === "View Pay-As-You-Go") { - vscode.env.openExternal(vscode.Uri.parse("https://trypear.ai/pay-as-you-go")) - } - }) - } else { - vscode.window.showInformationMessage(warningMsg) + if (warningMsg) { + if (warningMsg.includes("pay-as-you-go")) { + vscode.window.showInformationMessage(warningMsg, "View Pay-As-You-Go").then((selection) => { + if (selection === "View Pay-As-You-Go") { + vscode.env.openExternal(vscode.Uri.parse("https://trypear.ai/pay-as-you-go")) + } + }) + } else { + vscode.window.showInformationMessage(warningMsg) + } } + } catch (e) { + const errorMessage = e instanceof Error ? e.message : String(e) + vscode.window.showWarningMessage(`Notice: ${errorMessage}`) } } diff --git a/src/core/Cline.ts b/src/core/Cline.ts index d32581be9fa..a2e876f0007 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -11,7 +11,7 @@ import { serializeError } from "serialize-error" import * as vscode from "vscode" // schemas -import { TokenUsage, ToolUsage, ToolName, ModelInfo } from "../schemas" +import { TokenUsage, ToolUsage, ToolName, ModelInfo, CreatorModeConfig } from "../schemas" // api import { ApiHandler, buildApiHandler } from "../api" @@ -127,6 +127,7 @@ export type ClineOptions = { taskNumber?: number onCreated?: (cline: Cline) => void pearaiModels?: Record + creatorModeConfig?: CreatorModeConfig } export class Cline extends EventEmitter { @@ -142,6 +143,7 @@ export class Cline extends EventEmitter { pausedModeSlug: string = defaultModeSlug private pauseInterval: NodeJS.Timeout | undefined + public creatorModeConfig: CreatorModeConfig readonly apiConfiguration: ApiConfiguration api: ApiHandler private promptCacheKey: string @@ -220,6 +222,7 @@ export class Cline extends EventEmitter { parentTask, taskNumber = -1, onCreated, + creatorModeConfig, }: ClineOptions) { super() @@ -242,8 +245,13 @@ export class Cline extends EventEmitter { console.error("Failed to initialize RooIgnoreController:", error) }) - this.apiConfiguration = apiConfiguration - this.api = buildApiHandler(apiConfiguration) + this.creatorModeConfig = creatorModeConfig ?? historyItem?.creatorModeConfig ?? { creatorMode: false } + + this.apiConfiguration = { + ...apiConfiguration, + creatorModeConfig: this.creatorModeConfig + } + this.api = buildApiHandler(this.apiConfiguration) this.promptCacheKey = crypto.randomUUID() this.urlContentFetcher = new UrlContentFetcher(provider.context) @@ -257,6 +265,8 @@ export class Cline extends EventEmitter { this.diffViewProvider = new DiffViewProvider(this.cwd) this.enableCheckpoints = enableCheckpoints + this.creatorModeConfig = creatorModeConfig ?? historyItem?.creatorModeConfig ?? { creatorMode: false } + this.rootTask = rootTask this.parentTask = parentTask this.taskNumber = taskNumber @@ -371,6 +381,7 @@ export class Cline extends EventEmitter { taskNumber: this.taskNumber, globalStoragePath: this.globalStoragePath, workspace: this.cwd, + creatorModeConfig: this.creatorModeConfig, }) this.emit("taskTokenUsageUpdated", this.taskId, tokenUsage) @@ -1926,7 +1937,7 @@ export class Cline extends EventEmitter { // if there's no assistant_responses, that means we got no text or tool_use content blocks from API which we should assume is an error await this.say( "error", - "Unexpected API Response: The language model did not provide any assistant messages. This may indicate an issue with the API or the model's output.", + "Oops! Something went wrong. Please check the notifications on the bottom right of the window for more details, or contact PearAI Support on Discord.", ) await this.addToApiConversationHistory({ role: "assistant", diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts index eed7dee9485..69235e15e31 100644 --- a/src/core/config/CustomModesManager.ts +++ b/src/core/config/CustomModesManager.ts @@ -7,9 +7,9 @@ import { fileExistsAtPath } from "../../utils/fs" import { arePathsEqual, getWorkspacePath } from "../../utils/path" import { logger } from "../../utils/logging" import { GlobalFileNames } from "../../shared/globalFileNames" +import { AGENT_MODES_FILE_NAME } from "../../shared/constants" -const ROOMODES_FILENAME = ".roomodes" - +const ROOMODES_FILENAME = AGENT_MODES_FILE_NAME export class CustomModesManager { private static readonly cacheTTL = 10_000 @@ -154,11 +154,11 @@ export class CustomModesManager { return } - // Get modes from .roomodes if it exists (takes precedence) + // Get modes from .pearai-agent-modes if it exists (takes precedence) const roomodesPath = await this.getWorkspaceRoomodes() const roomodesModes = roomodesPath ? await this.loadModesFromFile(roomodesPath) : [] - // Merge modes from both sources (.roomodes takes precedence) + // Merge modes from both sources (.pearai-agent-modes takes precedence) const mergedModes = await this.mergeCustomModes(roomodesModes, result.data.customModes) await this.context.globalState.update("customModes", mergedModes) this.clearCache() @@ -167,7 +167,7 @@ export class CustomModesManager { }), ) - // Watch .roomodes file if it exists + // Watch .pearai-agent-modes file if it exists const roomodesPath = await this.getWorkspaceRoomodes() if (roomodesPath) { @@ -176,7 +176,7 @@ export class CustomModesManager { if (arePathsEqual(document.uri.fsPath, roomodesPath)) { const settingsModes = await this.loadModesFromFile(settingsPath) const roomodesModes = await this.loadModesFromFile(roomodesPath) - // .roomodes takes precedence + // .pearai-agent-modes takes precedence const mergedModes = await this.mergeCustomModes(roomodesModes, settingsModes) await this.context.globalState.update("customModes", mergedModes) this.clearCache() @@ -199,7 +199,7 @@ export class CustomModesManager { const settingsPath = await this.getCustomModesFilePath() const settingsModes = await this.loadModesFromFile(settingsPath) - // Get modes from .roomodes if it exists. + // Get modes from .pearai-agent-modes if it exists const roomodesPath = await this.getWorkspaceRoomodes() const roomodesModes = roomodesPath ? await this.loadModesFromFile(roomodesPath) : [] diff --git a/src/core/config/__tests__/CustomModesManager.test.ts b/src/core/config/__tests__/CustomModesManager.test.ts index 065f1478285..dabf4e4bcb7 100644 --- a/src/core/config/__tests__/CustomModesManager.test.ts +++ b/src/core/config/__tests__/CustomModesManager.test.ts @@ -23,7 +23,7 @@ describe("CustomModesManager", () => { // Use path.sep to ensure correct path separators for the current platform const mockStoragePath = `${path.sep}mock${path.sep}settings` const mockSettingsPath = path.join(mockStoragePath, "settings", GlobalFileNames.customModes) - const mockRoomodes = `${path.sep}mock${path.sep}workspace${path.sep}.roomodes` + const mockRoomodes = `${path.sep}mock${path.sep}workspace${path.sep}.pearai-agent-ignore` beforeEach(() => { mockOnUpdate = jest.fn() @@ -60,7 +60,7 @@ describe("CustomModesManager", () => { }) describe("getCustomModes", () => { - it("should merge modes with .roomodes taking precedence", async () => { + it("should merge modes with .pearai-agent-ignore taking precedence", async () => { const settingsModes = [ { slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }, { slug: "mode2", name: "Mode 2", roleDefinition: "Role 2", groups: ["read"] }, @@ -87,13 +87,13 @@ describe("CustomModesManager", () => { expect(modes).toHaveLength(3) expect(modes.map((m) => m.slug)).toEqual(["mode2", "mode3", "mode1"]) - // mode2 should come from .roomodes since it takes precedence + // mode2 should come from .pearai-agent-ignore since it takes precedence const mode2 = modes.find((m) => m.slug === "mode2") expect(mode2?.name).toBe("Mode 2 Override") expect(mode2?.roleDefinition).toBe("Role 2 Override") }) - it("should handle missing .roomodes file", async () => { + it("should handle missing .pearai-agent-ignore file", async () => { const settingsModes = [{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }] ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => { @@ -112,7 +112,7 @@ describe("CustomModesManager", () => { expect(modes[0].slug).toBe("mode1") }) - it("should handle invalid JSON in .roomodes", async () => { + it("should handle invalid JSON in .pearai-agent-ignore", async () => { const settingsModes = [{ slug: "mode1", name: "Mode 1", roleDefinition: "Role 1", groups: ["read"] }] ;(fs.readFile as jest.Mock).mockImplementation(async (path: string) => { @@ -127,7 +127,7 @@ describe("CustomModesManager", () => { const modes = await manager.getCustomModes() - // Should fall back to settings modes when .roomodes is invalid + // Should fall back to settings modes when .pearai-agent-ignore is invalid expect(modes).toHaveLength(1) expect(modes[0].slug).toBe("mode1") }) @@ -385,7 +385,7 @@ describe("CustomModesManager", () => { }) describe("updateCustomMode", () => { - it("should update mode in settings file while preserving .roomodes precedence", async () => { + it("should update mode in settings file while preserving .pearai-agent-ignore precedence", async () => { const newMode: ModeConfig = { slug: "mode1", name: "Updated Mode 1", @@ -449,13 +449,13 @@ describe("CustomModesManager", () => { }), ) - // Should update global state with merged modes where .roomodes takes precedence + // Should update global state with merged modes where .pearai-agent-ignore takes precedence expect(mockContext.globalState.update).toHaveBeenCalledWith( "customModes", expect.arrayContaining([ expect.objectContaining({ slug: "mode1", - name: "Roomodes Mode 1", // .roomodes version should take precedence + name: "Roomodes Mode 1", // .pearai-agent-ignore version should take precedence source: "project", }), ]), @@ -465,7 +465,7 @@ describe("CustomModesManager", () => { expect(mockOnUpdate).toHaveBeenCalled() }) - it("creates .roomodes file when adding project-specific mode", async () => { + it("creates .pearai-agent-ignore file when adding project-specific mode", async () => { const projectMode: ModeConfig = { slug: "project-mode", name: "Project Mode", @@ -474,7 +474,7 @@ describe("CustomModesManager", () => { source: "project", } - // Mock .roomodes to not exist initially + // Mock .pearai-agent-ignore to not exist initially let roomodesContent: any = null ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => { return path === mockSettingsPath @@ -500,7 +500,7 @@ describe("CustomModesManager", () => { await manager.updateCustomMode("project-mode", projectMode) - // Verify .roomodes was created with the project mode + // Verify .pearai-agent-ignore was created with the project mode expect(fs.writeFile).toHaveBeenCalledWith( expect.any(String), // Don't check exact path as it may have different separators on different platforms expect.stringContaining("project-mode"), @@ -511,7 +511,7 @@ describe("CustomModesManager", () => { const writeCall = (fs.writeFile as jest.Mock).mock.calls[0] expect(path.normalize(writeCall[0])).toBe(path.normalize(mockRoomodes)) - // Verify the content written to .roomodes + // Verify the content written to .pearai-agent-ignore expect(roomodesContent).toEqual({ customModes: [ expect.objectContaining({ diff --git a/src/core/ignore/RooIgnoreController.ts b/src/core/ignore/RooIgnoreController.ts index fda6c371757..cd794aa9850 100644 --- a/src/core/ignore/RooIgnoreController.ts +++ b/src/core/ignore/RooIgnoreController.ts @@ -3,13 +3,13 @@ import { fileExistsAtPath } from "../../utils/fs" import fs from "fs/promises" import ignore, { Ignore } from "ignore" import * as vscode from "vscode" +import { AGENT_IGNORE_FILE_NAME } from "../../shared/constants" export const LOCK_TEXT_SYMBOL = "\u{1F512}" - /** * Controls LLM access to files by enforcing ignore patterns. * Designed to be instantiated once in Cline.ts and passed to file manipulation services. - * Uses the 'ignore' library to support standard .gitignore syntax in .rooignore files. + * Uses the 'ignore' library to support standard .gitignore syntax in .pearai-agent-ignore files. */ export class RooIgnoreController { private cwd: string @@ -21,7 +21,7 @@ export class RooIgnoreController { this.cwd = cwd this.ignoreInstance = ignore() this.rooIgnoreContent = undefined - // Set up file watcher for .rooignore + // Set up file watcher for .pearai-agent-ignore this.setupFileWatcher() } @@ -34,10 +34,10 @@ export class RooIgnoreController { } /** - * Set up the file watcher for .rooignore changes + * Set up the file watcher for .pearai-agent-ignore changes */ private setupFileWatcher(): void { - const rooignorePattern = new vscode.RelativePattern(this.cwd, ".rooignore") + const rooignorePattern = new vscode.RelativePattern(this.cwd, AGENT_IGNORE_FILE_NAME) const fileWatcher = vscode.workspace.createFileSystemWatcher(rooignorePattern) // Watch for changes and updates @@ -58,24 +58,24 @@ export class RooIgnoreController { } /** - * Load custom patterns from .rooignore if it exists + * Load custom patterns from .pearai-agent-ignore if it exists */ private async loadRooIgnore(): Promise { try { // Reset ignore instance to prevent duplicate patterns this.ignoreInstance = ignore() - const ignorePath = path.join(this.cwd, ".rooignore") + const ignorePath = path.join(this.cwd, AGENT_IGNORE_FILE_NAME) if (await fileExistsAtPath(ignorePath)) { const content = await fs.readFile(ignorePath, "utf8") this.rooIgnoreContent = content this.ignoreInstance.add(content) - this.ignoreInstance.add(".rooignore") + this.ignoreInstance.add(AGENT_IGNORE_FILE_NAME) } else { this.rooIgnoreContent = undefined } } catch (error) { // Should never happen: reading file failed even though it exists - console.error("Unexpected error loading .rooignore:", error) + console.error(`Unexpected error loading ${AGENT_IGNORE_FILE_NAME}:`, error) } } @@ -85,7 +85,7 @@ export class RooIgnoreController { * @returns true if file is accessible, false if ignored */ validateAccess(filePath: string): boolean { - // Always allow access if .rooignore does not exist + // Always allow access if .pearai-agent-ignore does not exist if (!this.rooIgnoreContent) { return true } @@ -109,7 +109,7 @@ export class RooIgnoreController { * @returns path of file that is being accessed if it is being accessed, undefined if command is allowed */ validateCommand(command: string): string | undefined { - // Always allow if no .rooignore exists + // Always allow if no .pearai-agent-ignore exists if (!this.rooIgnoreContent) { return undefined } @@ -188,14 +188,14 @@ export class RooIgnoreController { } /** - * Get formatted instructions about the .rooignore file for the LLM - * @returns Formatted instructions or undefined if .rooignore doesn't exist + * Get formatted instructions about the .pearai-agent-ignore file for the LLM + * @returns Formatted instructions or undefined if .pearai-agent-ignore doesn't exist */ getInstructions(): string | undefined { if (!this.rooIgnoreContent) { return undefined } - return `# .rooignore\n\n(The following is provided by a root-level .rooignore file where the user has specified files and directories that should not be accessed. When using list_files, you'll notice a ${LOCK_TEXT_SYMBOL} next to files that are blocked. Attempting to access the file's contents e.g. through read_file will result in an error.)\n\n${this.rooIgnoreContent}\n.rooignore` + return `# ${AGENT_IGNORE_FILE_NAME}\n\n(The following is provided by a root-level ${AGENT_IGNORE_FILE_NAME} file where the user has specified files and directories that should not be accessed. When using list_files, you'll notice a ${LOCK_TEXT_SYMBOL} next to files that are blocked. Attempting to access the file's contents e.g. through read_file will result in an error.)\n\n${this.rooIgnoreContent}\n${AGENT_IGNORE_FILE_NAME}` } } diff --git a/src/core/ignore/__tests__/RooIgnoreController.security.test.ts b/src/core/ignore/__tests__/RooIgnoreController.security.test.ts index c71c1fcdb6f..91aca076e40 100644 --- a/src/core/ignore/__tests__/RooIgnoreController.security.test.ts +++ b/src/core/ignore/__tests__/RooIgnoreController.security.test.ts @@ -41,7 +41,7 @@ describe("RooIgnoreController Security Tests", () => { mockFileExists = fileExistsAtPath as jest.MockedFunction mockReadFile = fs.readFile as jest.MockedFunction - // By default, setup .rooignore to exist with some patterns + // By default, setup .pearai-agent-ignore to exist with some patterns mockFileExists.mockResolvedValue(true) mockReadFile.mockResolvedValue("node_modules\n.git\nsecrets/**\n*.log\nprivate/") diff --git a/src/core/prompts/__tests__/custom-system-prompt.test.ts b/src/core/prompts/__tests__/custom-system-prompt.test.ts index 977ab051a00..acaeca61a4c 100644 --- a/src/core/prompts/__tests__/custom-system-prompt.test.ts +++ b/src/core/prompts/__tests__/custom-system-prompt.test.ts @@ -3,6 +3,7 @@ import { defaultModeSlug, modes } from "../../../shared/modes" import * as vscode from "vscode" import * as fs from "fs/promises" import { toPosix } from "./utils" +import { AGENT_RULES_DIR } from "../../../shared/constants" // Mock the fs/promises module jest.mock("fs/promises", () => ({ @@ -90,7 +91,7 @@ describe("File-Based Custom System Prompt", () => { const fileCustomSystemPrompt = "Custom system prompt from file" // When called with utf-8 encoding, return a string mockedFs.readFile.mockImplementation((filePath, options) => { - if (toPosix(filePath).includes(`.roo/system-prompt-${defaultModeSlug}`) && options === "utf-8") { + if (toPosix(filePath).includes(`${AGENT_RULES_DIR}/system-prompt-${defaultModeSlug}`) && options === "utf-8") { return Promise.resolve(fileCustomSystemPrompt) } return Promise.reject({ code: "ENOENT" }) @@ -125,7 +126,7 @@ describe("File-Based Custom System Prompt", () => { // Mock the readFile to return content from a file const fileCustomSystemPrompt = "Custom system prompt from file" mockedFs.readFile.mockImplementation((filePath, options) => { - if (toPosix(filePath).includes(`.roo/system-prompt-${defaultModeSlug}`) && options === "utf-8") { + if (toPosix(filePath).includes(`${AGENT_RULES_DIR}/system-prompt-${defaultModeSlug}`) && options === "utf-8") { return Promise.resolve(fileCustomSystemPrompt) } return Promise.reject({ code: "ENOENT" }) diff --git a/src/core/prompts/__tests__/responses-rooignore.test.ts b/src/core/prompts/__tests__/responses-rooignore.test.ts index 46f1bec438a..518f07e4e20 100644 --- a/src/core/prompts/__tests__/responses-rooignore.test.ts +++ b/src/core/prompts/__tests__/responses-rooignore.test.ts @@ -50,9 +50,9 @@ describe("RooIgnore Response Formatting", () => { const errorMessage = formatResponse.rooIgnoreError("secrets/api-keys.json") // Verify error message format - expect(errorMessage).toContain("Access to secrets/api-keys.json is blocked by the .rooignore file settings") + expect(errorMessage).toContain("Access to secrets/api-keys.json is blocked by the .pearai-agent-ignore file settings") expect(errorMessage).toContain("continue in the task without using this file") - expect(errorMessage).toContain("ask the user to update the .rooignore file") + expect(errorMessage).toContain("ask the user to update the .pearai-agent-ignore file") }) /** @@ -207,7 +207,7 @@ describe("RooIgnore Response Formatting", () => { /** * Tests the instructions format */ - it("should format .rooignore instructions for the LLM", async () => { + it("should format .pearai-agent-ignore instructions for the LLM", async () => { // Create controller const controller = new RooIgnoreController(TEST_CWD) await controller.initialize() @@ -216,7 +216,7 @@ describe("RooIgnore Response Formatting", () => { const instructions = controller.getInstructions() // Verify format and content - expect(instructions).toContain("# .rooignore") + expect(instructions).toContain("# .pearai-agent-ignore") expect(instructions).toContain(LOCK_TEXT_SYMBOL) expect(instructions).toContain("node_modules") expect(instructions).toContain(".git") @@ -231,11 +231,11 @@ describe("RooIgnore Response Formatting", () => { /** * Tests null/undefined case */ - it("should return undefined when no .rooignore exists", async () => { - // Set up no .rooignore + it("should return undefined when no .pearai-agent-ignore exists", async () => { + // Set up no .pearai-agent-ignore mockFileExists.mockResolvedValue(false) - // Create controller without .rooignore + // Create controller without .pearai-agent-ignore const controller = new RooIgnoreController(TEST_CWD) await controller.initialize() diff --git a/src/core/prompts/instructions/create-mode.ts b/src/core/prompts/instructions/create-mode.ts index 598e2fccbf4..ad02f6b6ab5 100644 --- a/src/core/prompts/instructions/create-mode.ts +++ b/src/core/prompts/instructions/create-mode.ts @@ -2,6 +2,7 @@ import * as path from "path" import * as vscode from "vscode" import { GlobalFileNames } from "../../../shared/globalFileNames" +import { AGENT_MODES_FILE_NAME } from "../../../shared/constants" export async function createModeInstructions(context: vscode.ExtensionContext | undefined): Promise { if (!context) throw new Error("Missing VSCode Extension Context") @@ -12,12 +13,12 @@ export async function createModeInstructions(context: vscode.ExtensionContext | return ` Custom modes can be configured in two ways: 1. Globally via '${customModesPath}' (created automatically on startup) - 2. Per-workspace via '.roomodes' in the workspace root directory + 2. Per-workspace via '${AGENT_MODES_FILE_NAME}' in the workspace root directory -When modes with the same slug exist in both files, the workspace-specific .roomodes version takes precedence. This allows projects to override global modes or define project-specific modes. +When modes with the same slug exist in both files, the workspace-specific ${AGENT_MODES_FILE_NAME} version takes precedence. This allows projects to override global modes or define project-specific modes. -If asked to create a project mode, create it in .roomodes in the workspace root. If asked to create a global mode, use the global custom modes file. +If asked to create a project mode, create it in ${AGENT_MODES_FILE_NAME} in the workspace root. If asked to create a global mode, use the global custom modes file. - The following fields are required and must not be empty: * slug: A valid slug (lowercase letters, numbers, and hyphens). Must be unique, and shorter is better. diff --git a/src/core/prompts/responses.ts b/src/core/prompts/responses.ts index 314387171af..9c82a848804 100644 --- a/src/core/prompts/responses.ts +++ b/src/core/prompts/responses.ts @@ -2,6 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import * as path from "path" import * as diff from "diff" import { RooIgnoreController, LOCK_TEXT_SYMBOL } from "../ignore/RooIgnoreController" +import { AGENT_IGNORE_FILE_NAME } from "../../shared/constants" export const formatResponse = { toolDenied: () => `The user denied this operation.`, @@ -15,7 +16,7 @@ export const formatResponse = { toolError: (error?: string) => `The tool execution failed with the following error:\n\n${error}\n`, rooIgnoreError: (path: string) => - `Access to ${path} is blocked by the .rooignore file settings. You must try to continue in the task without using this file, or ask the user to update the .rooignore file.`, + `Access to ${path} is blocked by the ${AGENT_IGNORE_FILE_NAME} file settings. You must try to continue in the task without using this file, or ask the user to update the ${AGENT_IGNORE_FILE_NAME} file.`, noToolsUsed: () => `[ERROR] You did not use a tool in your previous response! Please retry with a tool use. diff --git a/src/core/prompts/sections/__tests__/custom-instructions.test.ts b/src/core/prompts/sections/__tests__/custom-instructions.test.ts index e243526d210..5a4ef4cbfcd 100644 --- a/src/core/prompts/sections/__tests__/custom-instructions.test.ts +++ b/src/core/prompts/sections/__tests__/custom-instructions.test.ts @@ -42,7 +42,7 @@ describe("loadRuleFiles", () => { }) it("should read and trim file content", async () => { - // Simulate no .roo/rules directory + // Simulate no .pearai-agent/rules directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockResolvedValue(" content with spaces ") const result = await loadRuleFiles("/fake/path") @@ -51,7 +51,7 @@ describe("loadRuleFiles", () => { }) it("should handle ENOENT error", async () => { - // Simulate no .roo/rules directory + // Simulate no .pearai-agent/rules directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockRejectedValue({ code: "ENOENT" }) const result = await loadRuleFiles("/fake/path") @@ -59,7 +59,7 @@ describe("loadRuleFiles", () => { }) it("should handle EISDIR error", async () => { - // Simulate no .roo/rules directory + // Simulate no .pearai-agent/rules directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockRejectedValue({ code: "EISDIR" }) const result = await loadRuleFiles("/fake/path") @@ -67,7 +67,7 @@ describe("loadRuleFiles", () => { }) it("should throw on unexpected errors", async () => { - // Simulate no .roo/rules directory + // Simulate no .pearai-agent/rules directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) const error = new Error("Permission denied") as NodeJS.ErrnoException error.code = "EPERM" @@ -79,7 +79,7 @@ describe("loadRuleFiles", () => { }) it("should not combine content from multiple rule files when they exist", async () => { - // Simulate no .roo/rules directory + // Simulate no .pearai-agent/rules directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockImplementation((filePath: PathLike) => { if (filePath.toString().endsWith(".roorules")) { @@ -96,7 +96,7 @@ describe("loadRuleFiles", () => { }) it("should handle when no rule files exist", async () => { - // Simulate no .roo/rules directory + // Simulate no .pearai-agent/rules directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockRejectedValue({ code: "ENOENT" }) @@ -105,7 +105,7 @@ describe("loadRuleFiles", () => { }) it("should skip directories with same name as rule files", async () => { - // Simulate no .roo/rules directory + // Simulate no .pearai-agent/rules directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockImplementation((filePath: PathLike) => { if (filePath.toString().endsWith(".roorules")) { @@ -121,16 +121,16 @@ describe("loadRuleFiles", () => { expect(result).toBe("") }) - it("should use .roo/rules/ directory when it exists and has files", async () => { - // Simulate .roo/rules directory exists + it("should use .pearai-agent/rules/ directory when it exists and has files", async () => { + // Simulate .pearai-agent/rules directory exists statMock.mockResolvedValueOnce({ isDirectory: jest.fn().mockReturnValue(true), } as any) // Simulate listing files readdirMock.mockResolvedValueOnce([ - { name: "file1.txt", isFile: () => true, isSymbolicLink: () => false, parentPath: "/fake/path/.roo/rules" }, - { name: "file2.txt", isFile: () => true, isSymbolicLink: () => false, parentPath: "/fake/path/.roo/rules" }, + { name: "file1.txt", isFile: () => true, isSymbolicLink: () => false, parentPath: "/fake/path/.pearai-agent/rules" }, + { name: "file2.txt", isFile: () => true, isSymbolicLink: () => false, parentPath: "/fake/path/.pearai-agent/rules" }, ] as any) statMock.mockImplementation( @@ -141,31 +141,31 @@ describe("loadRuleFiles", () => { ) readFileMock.mockImplementation((filePath: PathLike) => { - if (filePath.toString() === "/fake/path/.roo/rules/file1.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules/file1.txt") { return Promise.resolve("content of file1") } - if (filePath.toString() === "/fake/path/.roo/rules/file2.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules/file2.txt") { return Promise.resolve("content of file2") } return Promise.reject({ code: "ENOENT" }) }) const result = await loadRuleFiles("/fake/path") - expect(result).toContain("# Rules from /fake/path/.roo/rules/file1.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/file1.txt:") expect(result).toContain("content of file1") - expect(result).toContain("# Rules from /fake/path/.roo/rules/file2.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/file2.txt:") expect(result).toContain("content of file2") // We expect both checks because our new implementation checks the files again for validation - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules") - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules/file1.txt") - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules/file2.txt") - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/file1.txt", "utf-8") - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/file2.txt", "utf-8") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/file1.txt") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/file2.txt") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/file1.txt", "utf-8") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/file2.txt", "utf-8") }) - it("should fall back to .roorules when .roo/rules/ is empty", async () => { - // Simulate .roo/rules directory exists + it("should fall back to .roorules when .pearai-agent/rules/ is empty", async () => { + // Simulate .pearai-agent/rules directory exists statMock.mockResolvedValueOnce({ isDirectory: jest.fn().mockReturnValue(true), } as any) @@ -186,7 +186,7 @@ describe("loadRuleFiles", () => { }) it("should handle errors when reading directory", async () => { - // Simulate .roo/rules directory exists + // Simulate .pearai-agent/rules directory exists statMock.mockResolvedValueOnce({ isDirectory: jest.fn().mockReturnValue(true), } as any) @@ -206,8 +206,8 @@ describe("loadRuleFiles", () => { expect(result).toBe("\n# Rules from .roorules:\nroo rules content\n") }) - it("should read files from nested subdirectories in .roo/rules/", async () => { - // Simulate .roo/rules directory exists + it("should read files from nested subdirectories in .pearai-agent/rules/", async () => { + // Simulate .pearai-agent/rules directory exists statMock.mockResolvedValueOnce({ isDirectory: jest.fn().mockReturnValue(true), } as any) @@ -219,28 +219,28 @@ describe("loadRuleFiles", () => { isFile: () => false, isSymbolicLink: () => false, isDirectory: () => true, - parentPath: "/fake/path/.roo/rules", + parentPath: "/fake/path/.pearai-agent/rules", }, { name: "root.txt", isFile: () => true, isSymbolicLink: () => false, isDirectory: () => false, - parentPath: "/fake/path/.roo/rules", + parentPath: "/fake/path/.pearai-agent/rules", }, { name: "nested1.txt", isFile: () => true, isSymbolicLink: () => false, isDirectory: () => false, - parentPath: "/fake/path/.roo/rules/subdir", + parentPath: "/fake/path/.pearai-agent/rules/subdir", }, { name: "nested2.txt", isFile: () => true, isSymbolicLink: () => false, isDirectory: () => false, - parentPath: "/fake/path/.roo/rules/subdir/subdir2", + parentPath: "/fake/path/.pearai-agent/rules/subdir/subdir2", }, ] as any) @@ -259,13 +259,13 @@ describe("loadRuleFiles", () => { readFileMock.mockImplementation((filePath: PathLike) => { const path = filePath.toString() - if (path === "/fake/path/.roo/rules/root.txt") { + if (path === "/fake/path/.pearai-agent/rules/root.txt") { return Promise.resolve("root file content") } - if (path === "/fake/path/.roo/rules/subdir/nested1.txt") { + if (path === "/fake/path/.pearai-agent/rules/subdir/nested1.txt") { return Promise.resolve("nested file 1 content") } - if (path === "/fake/path/.roo/rules/subdir/subdir2/nested2.txt") { + if (path === "/fake/path/.pearai-agent/rules/subdir/subdir2/nested2.txt") { return Promise.resolve("nested file 2 content") } return Promise.reject({ code: "ENOENT" }) @@ -274,24 +274,24 @@ describe("loadRuleFiles", () => { const result = await loadRuleFiles("/fake/path") // Check root file content - expect(result).toContain("# Rules from /fake/path/.roo/rules/root.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/root.txt:") expect(result).toContain("root file content") // Check nested files content - expect(result).toContain("# Rules from /fake/path/.roo/rules/subdir/nested1.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/subdir/nested1.txt:") expect(result).toContain("nested file 1 content") - expect(result).toContain("# Rules from /fake/path/.roo/rules/subdir/subdir2/nested2.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/subdir/subdir2/nested2.txt:") expect(result).toContain("nested file 2 content") // Verify correct paths were checked - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules/root.txt") - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules/subdir/nested1.txt") - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules/subdir/subdir2/nested2.txt") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/root.txt") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/subdir/nested1.txt") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/subdir/subdir2/nested2.txt") // Verify files were read with correct paths - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/root.txt", "utf-8") - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/subdir/nested1.txt", "utf-8") - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/subdir/subdir2/nested2.txt", "utf-8") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/root.txt", "utf-8") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/subdir/nested1.txt", "utf-8") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/subdir/subdir2/nested2.txt", "utf-8") }) }) @@ -301,7 +301,7 @@ describe("addCustomInstructions", () => { }) it("should combine all instruction types when provided", async () => { - // Simulate no .roo/rules-test-mode directory + // Simulate no .pearai-agent/rules-test-mode directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockResolvedValue("mode specific rules") @@ -323,7 +323,7 @@ describe("addCustomInstructions", () => { }) it("should return empty string when no instructions provided", async () => { - // Simulate no .roo/rules directory + // Simulate no .pearai-agent/rules directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockRejectedValue({ code: "ENOENT" }) @@ -333,7 +333,7 @@ describe("addCustomInstructions", () => { }) it("should handle missing mode-specific rules file", async () => { - // Simulate no .roo/rules-test-mode directory + // Simulate no .pearai-agent/rules-test-mode directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockRejectedValue({ code: "ENOENT" }) @@ -351,7 +351,7 @@ describe("addCustomInstructions", () => { }) it("should handle unknown language codes properly", async () => { - // Simulate no .roo/rules-test-mode directory + // Simulate no .pearai-agent/rules-test-mode directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockRejectedValue({ code: "ENOENT" }) @@ -370,7 +370,7 @@ describe("addCustomInstructions", () => { }) it("should throw on unexpected errors", async () => { - // Simulate no .roo/rules-test-mode directory + // Simulate no .pearai-agent/rules-test-mode directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) const error = new Error("Permission denied") as NodeJS.ErrnoException @@ -383,7 +383,7 @@ describe("addCustomInstructions", () => { }) it("should skip mode-specific rule files that are directories", async () => { - // Simulate no .roo/rules-test-mode directory + // Simulate no .pearai-agent/rules-test-mode directory statMock.mockRejectedValueOnce({ code: "ENOENT" }) readFileMock.mockImplementation((filePath: PathLike) => { @@ -405,8 +405,8 @@ describe("addCustomInstructions", () => { expect(result).not.toContain("Rules from .clinerules-test-mode") }) - it("should use .roo/rules-test-mode/ directory when it exists and has files", async () => { - // Simulate .roo/rules-test-mode directory exists + it("should use .pearai-agent/rules-test-mode/ directory when it exists and has files", async () => { + // Simulate .pearai-agent/rules-test-mode directory exists statMock.mockResolvedValueOnce({ isDirectory: jest.fn().mockReturnValue(true), } as any) @@ -417,13 +417,13 @@ describe("addCustomInstructions", () => { name: "rule1.txt", isFile: () => true, isSymbolicLink: () => false, - parentPath: "/fake/path/.roo/rules-test-mode", + parentPath: "/fake/path/.pearai-agent/rules-test-mode", }, { name: "rule2.txt", isFile: () => true, isSymbolicLink: () => false, - parentPath: "/fake/path/.roo/rules-test-mode", + parentPath: "/fake/path/.pearai-agent/rules-test-mode", }, ] as any) @@ -435,10 +435,10 @@ describe("addCustomInstructions", () => { ) readFileMock.mockImplementation((filePath: PathLike) => { - if (filePath.toString() === "/fake/path/.roo/rules-test-mode/rule1.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules-test-mode/rule1.txt") { return Promise.resolve("mode specific rule 1") } - if (filePath.toString() === "/fake/path/.roo/rules-test-mode/rule2.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules-test-mode/rule2.txt") { return Promise.resolve("mode specific rule 2") } return Promise.reject({ code: "ENOENT" }) @@ -452,21 +452,21 @@ describe("addCustomInstructions", () => { { language: "es" }, ) - expect(result).toContain("# Rules from /fake/path/.roo/rules-test-mode") - expect(result).toContain("# Rules from /fake/path/.roo/rules-test-mode/rule1.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules-test-mode") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules-test-mode/rule1.txt:") expect(result).toContain("mode specific rule 1") - expect(result).toContain("# Rules from /fake/path/.roo/rules-test-mode/rule2.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules-test-mode/rule2.txt:") expect(result).toContain("mode specific rule 2") - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules-test-mode") - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules-test-mode/rule1.txt") - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules-test-mode/rule2.txt") - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules-test-mode/rule1.txt", "utf-8") - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules-test-mode/rule2.txt", "utf-8") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules-test-mode") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules-test-mode/rule1.txt") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules-test-mode/rule2.txt") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules-test-mode/rule1.txt", "utf-8") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules-test-mode/rule2.txt", "utf-8") }) - it("should fall back to .roorules-test-mode when .roo/rules-test-mode/ does not exist", async () => { - // Simulate .roo/rules-test-mode directory does not exist + it("should fall back to .roorules-test-mode when .pearai-agent/rules-test-mode/ does not exist", async () => { + // Simulate .pearai-agent/rules-test-mode directory does not exist statMock.mockRejectedValueOnce({ code: "ENOENT" }) // Simulate .roorules-test-mode exists @@ -487,8 +487,8 @@ describe("addCustomInstructions", () => { expect(result).toContain("Rules from .roorules-test-mode:\nmode specific rules from file") }) - it("should fall back to .clinerules-test-mode when .roo/rules-test-mode/ and .roorules-test-mode do not exist", async () => { - // Simulate .roo/rules-test-mode directory does not exist + it("should fall back to .clinerules-test-mode when .pearai-agent/rules-test-mode/ and .roorules-test-mode do not exist", async () => { + // Simulate .pearai-agent/rules-test-mode directory does not exist statMock.mockRejectedValueOnce({ code: "ENOENT" }) // Simulate file reading @@ -512,12 +512,12 @@ describe("addCustomInstructions", () => { expect(result).toContain("Rules from .clinerules-test-mode:\nmode specific rules from cline file") }) - it("should correctly format content from directories when using .roo/rules-test-mode/", async () => { + it("should correctly format content from directories when using .pearai-agent/rules-test-mode/", async () => { // Need to reset mockImplementation first to avoid interference from previous tests statMock.mockReset() readFileMock.mockReset() - // Simulate .roo/rules-test-mode directory exists + // Simulate .pearai-agent/rules-test-mode directory exists statMock.mockImplementationOnce(() => Promise.resolve({ isDirectory: jest.fn().mockReturnValue(true), @@ -526,7 +526,7 @@ describe("addCustomInstructions", () => { // Simulate directory has files readdirMock.mockResolvedValueOnce([ - { name: "rule1.txt", isFile: () => true, parentPath: "/fake/path/.roo/rules-test-mode" }, + { name: "rule1.txt", isFile: () => true, parentPath: "/fake/path/.pearai-agent/rules-test-mode" }, ] as any) readFileMock.mockReset() @@ -534,7 +534,7 @@ describe("addCustomInstructions", () => { let statCallCount = 0 statMock.mockImplementation((filePath) => { statCallCount++ - if (filePath === "/fake/path/.roo/rules-test-mode/rule1.txt") { + if (filePath === "/fake/path/.pearai-agent/rules-test-mode/rule1.txt") { return Promise.resolve({ isFile: jest.fn().mockReturnValue(true), isDirectory: jest.fn().mockReturnValue(false), @@ -547,7 +547,7 @@ describe("addCustomInstructions", () => { }) readFileMock.mockImplementation((filePath: PathLike) => { - if (filePath.toString() === "/fake/path/.roo/rules-test-mode/rule1.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules-test-mode/rule1.txt") { return Promise.resolve("mode specific rule content") } return Promise.reject({ code: "ENOENT" }) @@ -560,8 +560,8 @@ describe("addCustomInstructions", () => { "test-mode", ) - expect(result).toContain("# Rules from /fake/path/.roo/rules-test-mode") - expect(result).toContain("# Rules from /fake/path/.roo/rules-test-mode/rule1.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules-test-mode") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules-test-mode/rule1.txt:") expect(result).toContain("mode specific rule content") expect(statCallCount).toBeGreaterThan(0) @@ -589,7 +589,7 @@ describe("Directory existence checks", () => { await loadRuleFiles("/fake/path") // Verify stat was called to check directory existence - expect(statMock).toHaveBeenCalledWith("/fake/path/.roo/rules") + expect(statMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules") }) it("should handle when directory does not exist", async () => { @@ -609,7 +609,7 @@ describe("Directory existence checks", () => { // Indirectly test readTextFilesFromDirectory and formatDirectoryContent through loadRuleFiles describe("Rules directory reading", () => { it("should follow symbolic links in the rules directory", async () => { - // Simulate .roo/rules directory exists + // Simulate .pearai-agent/rules directory exists statMock.mockResolvedValueOnce({ isDirectory: jest.fn().mockReturnValue(true), } as any) @@ -621,29 +621,29 @@ describe("Rules directory reading", () => { name: "regular.txt", isFile: () => true, isSymbolicLink: () => false, - parentPath: "/fake/path/.roo/rules", + parentPath: "/fake/path/.pearai-agent/rules", }, { name: "link.txt", isFile: () => false, isSymbolicLink: () => true, - parentPath: "/fake/path/.roo/rules", + parentPath: "/fake/path/.pearai-agent/rules", }, { name: "link_dir", isFile: () => false, isSymbolicLink: () => true, - parentPath: "/fake/path/.roo/rules", + parentPath: "/fake/path/.pearai-agent/rules", }, { name: "nested_link.txt", isFile: () => false, isSymbolicLink: () => true, - parentPath: "/fake/path/.roo/rules", + parentPath: "/fake/path/.pearai-agent/rules", }, ] as any) .mockResolvedValueOnce([ - { name: "subdir_link.txt", isFile: () => true, parentPath: "/fake/path/.roo/rules/symlink-target-dir" }, + { name: "subdir_link.txt", isFile: () => true, parentPath: "/fake/path/.pearai-agent/rules/symlink-target-dir" }, ] as any) // Simulate readlink response @@ -657,7 +657,7 @@ describe("Rules directory reading", () => { statMock.mockReset() statMock.mockImplementation((path: string) => { // For directory check - if (path === "/fake/path/.roo/rules" || path.endsWith("dir")) { + if (path === "/fake/path/.pearai-agent/rules" || path.endsWith("dir")) { return Promise.resolve({ isDirectory: jest.fn().mockReturnValue(true), isFile: jest.fn().mockReturnValue(false), @@ -682,16 +682,16 @@ describe("Rules directory reading", () => { // Simulate file content reading readFileMock.mockImplementation((filePath: PathLike) => { - if (filePath.toString() === "/fake/path/.roo/rules/regular.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules/regular.txt") { return Promise.resolve("regular file content") } - if (filePath.toString() === "/fake/path/.roo/rules/../symlink-target.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules/../symlink-target.txt") { return Promise.resolve("symlink target content") } - if (filePath.toString() === "/fake/path/.roo/rules/symlink-target-dir/subdir_link.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules/symlink-target-dir/subdir_link.txt") { return Promise.resolve("regular file content under symlink target dir") } - if (filePath.toString() === "/fake/path/.roo/rules/../nested-symlink-target.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules/../nested-symlink-target.txt") { return Promise.resolve("nested symlink target content") } return Promise.reject({ code: "ENOENT" }) @@ -700,47 +700,47 @@ describe("Rules directory reading", () => { const result = await loadRuleFiles("/fake/path") // Verify both regular file and symlink target content are included - expect(result).toContain("# Rules from /fake/path/.roo/rules/regular.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/regular.txt:") expect(result).toContain("regular file content") - expect(result).toContain("# Rules from /fake/path/.roo/rules/../symlink-target.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/../symlink-target.txt:") expect(result).toContain("symlink target content") - expect(result).toContain("# Rules from /fake/path/.roo/rules/symlink-target-dir/subdir_link.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/symlink-target-dir/subdir_link.txt:") expect(result).toContain("regular file content under symlink target dir") - expect(result).toContain("# Rules from /fake/path/.roo/rules/../nested-symlink-target.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/../nested-symlink-target.txt:") expect(result).toContain("nested symlink target content") // Verify readlink was called with the symlink path - expect(readlinkMock).toHaveBeenCalledWith("/fake/path/.roo/rules/link.txt") - expect(readlinkMock).toHaveBeenCalledWith("/fake/path/.roo/rules/link_dir") + expect(readlinkMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/link.txt") + expect(readlinkMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/link_dir") // Verify both files were read - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/regular.txt", "utf-8") - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/../symlink-target.txt", "utf-8") - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/symlink-target-dir/subdir_link.txt", "utf-8") - expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/../nested-symlink-target.txt", "utf-8") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/regular.txt", "utf-8") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/../symlink-target.txt", "utf-8") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/symlink-target-dir/subdir_link.txt", "utf-8") + expect(readFileMock).toHaveBeenCalledWith("/fake/path/.pearai-agent/rules/../nested-symlink-target.txt", "utf-8") }) beforeEach(() => { jest.clearAllMocks() }) it("should correctly format multiple files from directory", async () => { - // Simulate .roo/rules directory exists + // Simulate .pearai-agent/rules directory exists statMock.mockResolvedValueOnce({ isDirectory: jest.fn().mockReturnValue(true), } as any) // Simulate listing files readdirMock.mockResolvedValueOnce([ - { name: "file1.txt", isFile: () => true, parentPath: "/fake/path/.roo/rules" }, - { name: "file2.txt", isFile: () => true, parentPath: "/fake/path/.roo/rules" }, - { name: "file3.txt", isFile: () => true, parentPath: "/fake/path/.roo/rules" }, + { name: "file1.txt", isFile: () => true, parentPath: "/fake/path/.pearai-agent/rules" }, + { name: "file2.txt", isFile: () => true, parentPath: "/fake/path/.pearai-agent/rules" }, + { name: "file3.txt", isFile: () => true, parentPath: "/fake/path/.pearai-agent/rules" }, ] as any) statMock.mockImplementation((path) => { expect([ - "/fake/path/.roo/rules/file1.txt", - "/fake/path/.roo/rules/file2.txt", - "/fake/path/.roo/rules/file3.txt", + "/fake/path/.pearai-agent/rules/file1.txt", + "/fake/path/.pearai-agent/rules/file2.txt", + "/fake/path/.pearai-agent/rules/file3.txt", ]).toContain(path) return Promise.resolve({ @@ -749,13 +749,13 @@ describe("Rules directory reading", () => { }) readFileMock.mockImplementation((filePath: PathLike) => { - if (filePath.toString() === "/fake/path/.roo/rules/file1.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules/file1.txt") { return Promise.resolve("content of file1") } - if (filePath.toString() === "/fake/path/.roo/rules/file2.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules/file2.txt") { return Promise.resolve("content of file2") } - if (filePath.toString() === "/fake/path/.roo/rules/file3.txt") { + if (filePath.toString() === "/fake/path/.pearai-agent/rules/file3.txt") { return Promise.resolve("content of file3") } return Promise.reject({ code: "ENOENT" }) @@ -763,16 +763,16 @@ describe("Rules directory reading", () => { const result = await loadRuleFiles("/fake/path") - expect(result).toContain("# Rules from /fake/path/.roo/rules/file1.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/file1.txt:") expect(result).toContain("content of file1") - expect(result).toContain("# Rules from /fake/path/.roo/rules/file2.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/file2.txt:") expect(result).toContain("content of file2") - expect(result).toContain("# Rules from /fake/path/.roo/rules/file3.txt:") + expect(result).toContain("# Rules from /fake/path/.pearai-agent/rules/file3.txt:") expect(result).toContain("content of file3") }) it("should handle empty file list gracefully", async () => { - // Simulate .roo/rules directory exists + // Simulate .pearai-agent/rules directory exists statMock.mockResolvedValueOnce({ isDirectory: jest.fn().mockReturnValue(true), } as any) diff --git a/src/core/prompts/sections/__tests__/custom-system-prompt.test.ts b/src/core/prompts/sections/__tests__/custom-system-prompt.test.ts index 9fc538860ae..b1ae557bd4c 100644 --- a/src/core/prompts/sections/__tests__/custom-system-prompt.test.ts +++ b/src/core/prompts/sections/__tests__/custom-system-prompt.test.ts @@ -2,6 +2,7 @@ import path from "path" import { readFile } from "fs/promises" import { Mode } from "../../../../shared/modes" // Adjusted import path import { loadSystemPromptFile, PromptVariables } from "../custom-system-prompt" +import { AGENT_RULES_DIR } from "../../../../shared/constants" // Mock the fs/promises module jest.mock("fs/promises") @@ -17,7 +18,7 @@ describe("loadSystemPromptFile", () => { const mockCwd = "/mock/cwd" const mockMode: Mode = "test" // Use Mode type, e.g., 'test' // Corrected expected file path format - const expectedFilePath = path.join(mockCwd, ".roo", `system-prompt-${mockMode}`) + const expectedFilePath = path.join(mockCwd, AGENT_RULES_DIR, `system-prompt-${mockMode}`) beforeEach(() => { // Clear mocks before each test diff --git a/src/core/prompts/sections/custom-instructions.ts b/src/core/prompts/sections/custom-instructions.ts index cf1aea24ff4..4f09ce7452c 100644 --- a/src/core/prompts/sections/custom-instructions.ts +++ b/src/core/prompts/sections/custom-instructions.ts @@ -3,6 +3,7 @@ import path from "path" import { LANGUAGES, isLanguage } from "../../../shared/language" import { Dirent } from "fs" +import { AGENT_RULES_DIR } from "../../../shared/constants" /** * Safely read a file and return its trimmed content @@ -156,8 +157,8 @@ function formatDirectoryContent(dirPath: string, files: Array<{ filename: string * Load rule files from the specified directory */ export async function loadRuleFiles(cwd: string): Promise { - // Check for .roo/rules/ directory - const rooRulesDir = path.join(cwd, ".roo", "rules") + // Check for .pearai-agent/rules/ directory + const rooRulesDir = path.join(cwd, AGENT_RULES_DIR, "rules") if (await directoryExists(rooRulesDir)) { const files = await readTextFilesFromDirectory(rooRulesDir) if (files.length > 0) { @@ -192,8 +193,8 @@ export async function addCustomInstructions( let usedRuleFile = "" if (mode) { - // Check for .roo/rules-${mode}/ directory - const modeRulesDir = path.join(cwd, ".roo", `rules-${mode}`) + // Check for .pearai-agent/rules-${mode}/ directory + const modeRulesDir = path.join(cwd, AGENT_RULES_DIR, `rules-${mode}`) if (await directoryExists(modeRulesDir)) { const files = await readTextFilesFromDirectory(modeRulesDir) if (files.length > 0) { @@ -241,7 +242,7 @@ export async function addCustomInstructions( // Add mode-specific rules first if they exist if (modeRuleContent && modeRuleContent.trim()) { - if (usedRuleFile.includes(path.join(".roo", `rules-${mode}`))) { + if (usedRuleFile.includes(path.join(AGENT_RULES_DIR, `rules-${mode}`))) { rules.push(modeRuleContent.trim()) } else { rules.push(`# Rules from ${usedRuleFile}:\n${modeRuleContent}`) diff --git a/src/core/prompts/sections/custom-system-prompt.ts b/src/core/prompts/sections/custom-system-prompt.ts index f401000bb55..8a0c746ffeb 100644 --- a/src/core/prompts/sections/custom-system-prompt.ts +++ b/src/core/prompts/sections/custom-system-prompt.ts @@ -2,6 +2,7 @@ import fs from "fs/promises" import path from "path" import { Mode } from "../../../shared/modes" import { fileExistsAtPath } from "../../../utils/fs" +import { AGENT_RULES_DIR } from "../../../shared/constants" export type PromptVariables = { workspace?: string @@ -46,11 +47,11 @@ async function safeReadFile(filePath: string): Promise { * Get the path to a system prompt file for a specific mode */ export function getSystemPromptFilePath(cwd: string, mode: Mode): string { - return path.join(cwd, ".roo", `system-prompt-${mode}`) + return path.join(cwd, AGENT_RULES_DIR, `system-prompt-${mode}`) } /** - * Loads custom system prompt from a file at .roo/system-prompt-[mode slug] + * Loads custom system prompt from a file at .pearai-agent/system-prompt-[mode slug] * If the file doesn't exist, returns an empty string */ export async function loadSystemPromptFile(cwd: string, mode: Mode, variables: PromptVariables): Promise { @@ -64,10 +65,10 @@ export async function loadSystemPromptFile(cwd: string, mode: Mode, variables: P } /** - * Ensures the .roo directory exists, creating it if necessary + * Ensures the .pearai-agent directory exists, creating it if necessary */ export async function ensureRooDirectory(cwd: string): Promise { - const rooDir = path.join(cwd, ".roo") + const rooDir = path.join(cwd, AGENT_RULES_DIR) // Check if directory already exists if (await fileExistsAtPath(rooDir)) { diff --git a/src/core/task-persistence/taskMetadata.ts b/src/core/task-persistence/taskMetadata.ts index 9784e622958..fb80ccd2113 100644 --- a/src/core/task-persistence/taskMetadata.ts +++ b/src/core/task-persistence/taskMetadata.ts @@ -17,6 +17,11 @@ export type TaskMetadataOptions = { taskNumber: number globalStoragePath: string workspace: string + creatorModeConfig?: { + creatorMode?: boolean + newProjectType?: string + newProjectPath?: string + } } export async function taskMetadata({ @@ -25,6 +30,7 @@ export async function taskMetadata({ taskNumber, globalStoragePath, workspace, + creatorModeConfig, }: TaskMetadataOptions) { const taskDir = await getTaskDirectoryPath(globalStoragePath, taskId) const taskMessage = messages[0] // First message is always the task say. @@ -57,6 +63,7 @@ export async function taskMetadata({ totalCost: tokenUsage.totalCost, size: taskDirSize, workspace, + creatorModeConfig, } return { historyItem, tokenUsage } diff --git a/src/core/tools/newTaskTool.ts b/src/core/tools/newTaskTool.ts index dc45c73d3a0..7b6030eee35 100644 --- a/src/core/tools/newTaskTool.ts +++ b/src/core/tools/newTaskTool.ts @@ -78,7 +78,13 @@ export async function newTaskTool( // Delay to allow mode change to take effect before next tool is executed. await delay(500) - const newCline = await provider.initClineWithTask(message, undefined, cline) + const newCline = await provider.initClineWithTask( + message, + undefined, + cline, + {}, + cline.creatorModeConfig + ) cline.emit("taskSpawned", newCline.taskId) pushToolResult(`Successfully created new task in ${targetMode.name} mode with message: ${message}`) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 23eb295c9eb..28fa9ec56fc 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -9,7 +9,7 @@ import axios from "axios" import pWaitFor from "p-wait-for" import * as vscode from "vscode" -import { GlobalState, ProviderSettings, RooCodeSettings } from "../../schemas" +import { CreatorModeConfig, GlobalState, ProviderSettings, RooCodeSettings } from "../../schemas" import { t } from "../../i18n" import { setPanel } from "../../activate/registerCommands" import { @@ -24,7 +24,7 @@ import { supportPrompt } from "../../shared/support-prompt" import { GlobalFileNames } from "../../shared/globalFileNames" import { HistoryItem } from "../../shared/HistoryItem" import { ExtensionMessage } from "../../shared/ExtensionMessage" -import { Mode, PromptComponent, defaultModeSlug } from "../../shared/modes" +import { Mode, PEARAI_CREATOR_MODE_WEBAPP_MANAGER_SLUG, PromptComponent, defaultModeSlug } from "../../shared/modes" import { experimentDefault } from "../../shared/experiments" import { formatLanguage } from "../../shared/language" import { Terminal } from "../../integrations/terminal/Terminal" @@ -86,6 +86,7 @@ export class ClineProvider extends EventEmitter implements private readonly outputChannel: vscode.OutputChannel, private readonly renderContext: "sidebar" | "editor" = "sidebar", public readonly contextProxy: ContextProxy, + private readonly isCreatorView: boolean = false, ) { super() @@ -115,6 +116,16 @@ export class ClineProvider extends EventEmitter implements }) } + public static getSidebarInstance(): ClineProvider | undefined { + const sidebar = Array.from(this.activeInstances).find((instance) => !instance.isCreatorView) + + if (!sidebar?.view?.visible) { + vscode.commands.executeCommand("pearai-roo-cline.SidebarProvider.focus") + } + + return sidebar + } + // Adds a new Cline instance to clineStack, marking the start of a new task. // The instance is pushed to the top of the stack (LIFO order). // When the task is completed, the top instance is removed, reactivating the previous task. @@ -453,12 +464,9 @@ export class ClineProvider extends EventEmitter implements return this.initClineWithTask(task, images, parent) } - // When initializing a new task, (not from history but from a tool command - // new_task) there is no need to remove the previouse task since the new - // task is a subtask of the previous one, and when it finishes it is removed - // from the stack and the caller is resumed in this way we can have a chain - // of tasks, each one being a sub task of the previous one until the main - // task is finished. + // when initializing a new task, (not from history but from a tool command new_task) there is no need to remove the previouse task + // since the new task is a sub task of the previous one, and when it finishes it is removed from the stack and the caller is resumed + // in this way we can have a chain of tasks, each one being a sub task of the previous one until the main task is finished public async initClineWithTask( task?: string, images?: string[], @@ -474,6 +482,7 @@ export class ClineProvider extends EventEmitter implements | "experiments" > > = {}, + creatorModeConfig?: CreatorModeConfig, ) { const { apiConfiguration, @@ -486,6 +495,15 @@ export class ClineProvider extends EventEmitter implements experiments, } = await this.getState() + // Update API configuration with creator mode + await this.updateApiConfiguration({ + ...apiConfiguration, + creatorModeConfig, + }) + + // Post updated state to webview immediately + await this.postStateToWebview() + const modePrompt = customModePrompts?.[mode] as PromptComponent const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n") @@ -493,7 +511,12 @@ export class ClineProvider extends EventEmitter implements const cline = new Cline({ provider: this, - apiConfiguration: { ...apiConfiguration, pearaiAgentModels: pearaiAgentModels }, + apiConfiguration: { + ...apiConfiguration, + creatorModeConfig, + pearaiAgentModels, + }, + creatorModeConfig, customInstructions: effectiveInstructions, enableDiff, enableCheckpoints, @@ -517,7 +540,10 @@ export class ClineProvider extends EventEmitter implements return cline } - public async initClineWithHistoryItem(historyItem: HistoryItem & { rootTask?: Cline; parentTask?: Cline }) { + public async initClineWithHistoryItem( + historyItem: HistoryItem & { rootTask?: Cline; parentTask?: Cline }, + options?: { creatorModeConfig?: CreatorModeConfig } + ) { await this.removeClineFromStack() const { @@ -548,6 +574,7 @@ export class ClineProvider extends EventEmitter implements parentTask: historyItem.parentTask, taskNumber: historyItem.number, onCreated: (cline) => this.emit("clineCreated", cline), + creatorModeConfig: options?.creatorModeConfig, }) await this.addClineToStack(cline) @@ -798,7 +825,24 @@ export class ClineProvider extends EventEmitter implements const config = listApiConfig?.find((c) => c.id === savedConfigId) if (config?.name) { - const apiConfig = await this.providerSettingsManager.loadConfig(config.name) + let apiConfig = await this.providerSettingsManager.loadConfig(config.name) + + // Switch to pearai-model-creator model if we are in Creator Mode + if (newMode == PEARAI_CREATOR_MODE_WEBAPP_MANAGER_SLUG) { + apiConfig = { + ...apiConfig, + apiProvider: "pearai", + apiModelId: "pearai-model-creator", + } + } else { + if (apiConfig.apiModelId == "pearai-model-creator") { + apiConfig = { + ...apiConfig, + apiProvider: "pearai", + apiModelId: "pearai-model", + } + } + } await Promise.all([ this.updateGlobalState("currentApiConfigName", config.name), @@ -824,6 +868,14 @@ export class ClineProvider extends EventEmitter implements async updateApiConfiguration(providerSettings: ProviderSettings) { // Update mode's default config. const { mode } = await this.getState() + const currentCline = this.getCurrentCline() + + // Preserve creator mode when updating configuration + const updatedConfig: ProviderSettings = { + ...providerSettings, + creatorModeConfig: currentCline?.creatorModeConfig, + } + if (mode) { const currentApiConfigName = this.getGlobalState("currentApiConfigName") @@ -835,10 +887,10 @@ export class ClineProvider extends EventEmitter implements } } - await this.contextProxy.setProviderSettings(providerSettings) + await this.contextProxy.setProviderSettings(updatedConfig) if (this.getCurrentCline()) { - this.getCurrentCline()!.api = buildApiHandler(providerSettings) + this.getCurrentCline()!.api = buildApiHandler(updatedConfig) } } @@ -855,6 +907,7 @@ export class ClineProvider extends EventEmitter implements // Preserve parent and root task information for history item. const rootTask = cline.rootTask const parentTask = cline.parentTask + const creatorModeConfig = cline.creatorModeConfig cline.abortTask() @@ -882,7 +935,7 @@ export class ClineProvider extends EventEmitter implements } // Clears task again, so we need to abortTask manually above. - await this.initClineWithHistoryItem({ ...historyItem, rootTask, parentTask }) + await this.initClineWithHistoryItem({ ...historyItem, rootTask, parentTask }, { creatorModeConfig }) } async updateCustomInstructions(instructions?: string) { @@ -1070,7 +1123,8 @@ export class ClineProvider extends EventEmitter implements if (id !== this.getCurrentCline()?.taskId) { // Non-current task. const { historyItem } = await this.getTaskWithId(id) - await this.initClineWithHistoryItem(historyItem) // Clears existing task. + const creatorModeConfig = this.getCurrentCline()?.creatorModeConfig + await this.initClineWithHistoryItem(historyItem, { creatorModeConfig }) // Clears existing task. } await this.postMessageToWebview({ type: "action", action: "chatButtonClicked" }) @@ -1150,8 +1204,10 @@ export class ClineProvider extends EventEmitter implements } async getStateToPostToWebview() { + const currentCline = this.getCurrentCline() + // Get base state const { - apiConfiguration, + apiConfiguration: baseApiConfiguration, lastShownAnnouncementId, customInstructions, alwaysAllowReadOnly, @@ -1211,7 +1267,12 @@ export class ClineProvider extends EventEmitter implements historyPreviewCollapsed, } = await this.getState() - const telemetryKey = process.env.POSTHOG_API_KEY + const creatorModeConfig = currentCline?.creatorModeConfig; + const apiConfiguration = { + ...baseApiConfiguration + } + + const telemetryKey = 'phc_RRjQ4roADRjH6xMbXDUDTA9WLeM5ePPvAJK19w3yj0z' const machineId = vscode.env.machineId const allowedCommands = vscode.workspace.getConfiguration("roo-cline").get("allowedCommands") || [] const cwd = this.cwd @@ -1296,6 +1357,7 @@ export class ClineProvider extends EventEmitter implements terminalCompressProgressBar: terminalCompressProgressBar ?? true, hasSystemPromptOverride, historyPreviewCollapsed: historyPreviewCollapsed ?? false, + creatorModeConfig, } } @@ -1531,6 +1593,11 @@ export class ClineProvider extends EventEmitter implements properties.diffStrategy = currentCline.diffStrategy.getName() } + // Add creator mode context if available + if (currentCline?.creatorModeConfig?.creatorMode) { + properties.isCreatorMode = true + } + return properties } } diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 2942bc43b11..344c9672186 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -10,6 +10,7 @@ import { setTtsEnabled } from "../../../utils/tts" import { defaultModeSlug } from "../../../shared/modes" import { experimentDefault } from "../../../shared/experiments" import { ContextProxy } from "../../config/ContextProxy" +import { AGENT_RULES_DIR } from "../../../shared/constants" // Mock setup must come before imports jest.mock("../../prompts/sections/custom-instructions") @@ -1980,7 +1981,7 @@ describe("Project MCP Settings", () => { // Verify directory was created expect(fs.mkdir).toHaveBeenCalledWith( - expect.stringContaining(".roo"), + expect.stringContaining(AGENT_RULES_DIR), expect.objectContaining({ recursive: true }), ) @@ -2023,7 +2024,7 @@ describe("Project MCP Settings", () => { // Verify error message was shown expect(vscode.window.showErrorMessage).toHaveBeenCalledWith( - expect.stringContaining("Failed to create or open .roo/mcp.json"), + expect.stringContaining(`Failed to create or open ${AGENT_RULES_DIR}/mcp.json`), ) }) }) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index ae14b5e1b53..003e2523a0c 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -38,6 +38,8 @@ import { buildApiHandler } from "../../api" import { GlobalState } from "../../schemas" import { MultiSearchReplaceDiffStrategy } from "../diff/strategies/multi-search-replace" import { getModels } from "../../api/providers/fetchers/cache" +import { getpearAIExports } from "../../activate/registerPearListener" +import { AGENT_RULES_DIR } from "../../shared/constants" export const webviewMessageHandler = async (provider: ClineProvider, message: WebviewMessage) => { // Utility functions provided for concise get/update of global state via contextProxy API. @@ -136,7 +138,10 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We // Could also do this in extension .ts //provider.postMessageToWebview({ type: "text", text: `Extension: ${Date.now()}` }) // initializing new instance of Cline will make sure that any agentically running promises in old instance don't affect our new task. this essentially creates a fresh slate for the new task - await provider.initClineWithTask(message.text, message.images) + const existingCline = provider.getCurrentCline() + const creatorModeConfig = existingCline?.creatorModeConfig + + await provider.initClineWithTask(message.text, message.images, undefined, {}, creatorModeConfig) break case "apiConfiguration": if (message.apiConfiguration) { @@ -394,7 +399,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We } const workspaceFolder = vscode.workspace.workspaceFolders[0] - const rooDir = path.join(workspaceFolder.uri.fsPath, ".roo") + const rooDir = path.join(workspaceFolder.uri.fsPath, AGENT_RULES_DIR) const mcpPath = path.join(rooDir, "mcp.json") try { @@ -1269,6 +1274,14 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We ), ) break + case "openPearAICreatorFeedbackOverlay": + const pearAIExports = await getpearAIExports(); + const currentCline = provider.getCurrentCline(); + + + // Open the feedback form with the chat history + pearAIExports.pearAPI.creatorMode.openFeedbackForm(currentCline?.clineMessages || []); + break } } diff --git a/src/exports/roo-code.d.ts b/src/exports/roo-code.d.ts index 4eef2426dab..d3a05f43ced 100644 --- a/src/exports/roo-code.d.ts +++ b/src/exports/roo-code.d.ts @@ -202,6 +202,13 @@ type ProviderSettings = { defaultModelId?: string | undefined } | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined } type GlobalSettings = { @@ -255,6 +262,13 @@ type GlobalSettings = { totalCost: number size?: number | undefined workspace?: string | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined }[] | undefined autoApprovalEnabled?: boolean | undefined @@ -350,6 +364,7 @@ type GlobalSettings = { ] )[] source?: ("global" | "project") | undefined + backendOnly?: boolean | undefined }[] | undefined customModePrompts?: diff --git a/src/exports/types.ts b/src/exports/types.ts index 0b557dd0baf..e964fdbe46c 100644 --- a/src/exports/types.ts +++ b/src/exports/types.ts @@ -203,6 +203,13 @@ type ProviderSettings = { defaultModelId?: string | undefined } | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined } export type { ProviderSettings } @@ -258,6 +265,13 @@ type GlobalSettings = { totalCost: number size?: number | undefined workspace?: string | undefined + creatorModeConfig?: + | { + creatorMode?: boolean | undefined + newProjectType?: string | undefined + newProjectPath?: string | undefined + } + | undefined }[] | undefined autoApprovalEnabled?: boolean | undefined @@ -353,6 +367,7 @@ type GlobalSettings = { ] )[] source?: ("global" | "project") | undefined + backendOnly?: boolean | undefined }[] | undefined customModePrompts?: diff --git a/src/extension.ts b/src/extension.ts index c391aaf0cd2..94085091801 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -26,7 +26,13 @@ import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry" import { API } from "./exports/api" import { migrateSettings } from "./utils/migrateSettings" -import { handleUri, registerCommands, registerCodeActions, registerTerminalActions } from "./activate" +import { + handleUri, + registerCommands, + registerCodeActions, + registerTerminalActions, + registerPearListener, +} from "./activate" import { formatLanguage } from "./shared/language" /** @@ -72,6 +78,8 @@ export async function activate(context: vscode.ExtensionContext) { const provider = new ClineProvider(context, outputChannel, "sidebar", contextProxy) telemetryService.setProvider(provider) + registerPearListener(provider); + context.subscriptions.push( vscode.window.registerWebviewViewProvider(ClineProvider.sideBarId, provider, { webviewOptions: { retainContextWhenHidden: true }, diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index 673d73687ea..3f9cd4bdb83 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -49,7 +49,7 @@ "list_api_config": "Ha fallat l'obtenció de la llista de configuracions de l'API", "update_server_timeout": "Ha fallat l'actualització del temps d'espera del servidor", "failed_update_project_mcp": "Ha fallat l'actualització dels servidors MCP del projecte", - "create_mcp_json": "Ha fallat la creació o obertura de .roo/mcp.json: {{error}}", + "create_mcp_json": "Ha fallat la creació o obertura de .pearai-agent/mcp.json: {{error}}", "hmr_not_running": "El servidor de desenvolupament local no està executant-se, l'HMR no funcionarà. Si us plau, executa 'npm run dev' abans de llançar l'extensió per habilitar l'HMR.", "retrieve_current_mode": "Error en recuperar el mode actual de l'estat.", "failed_delete_repo": "Ha fallat l'eliminació del repositori o branca associada: {{error}}", diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index cc62749a8d0..efee4e28623 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -45,7 +45,7 @@ "list_api_config": "Fehler beim Abrufen der API-Konfigurationsliste", "update_server_timeout": "Fehler beim Aktualisieren des Server-Timeouts", "failed_update_project_mcp": "Fehler beim Aktualisieren der Projekt-MCP-Server", - "create_mcp_json": "Fehler beim Erstellen oder Öffnen von .roo/mcp.json: {{error}}", + "create_mcp_json": "Fehler beim Erstellen oder Öffnen von .pearai-agent/mcp.json: {{error}}", "hmr_not_running": "Der lokale Entwicklungsserver läuft nicht, HMR wird nicht funktionieren. Bitte führen Sie 'npm run dev' vor dem Start der Erweiterung aus, um HMR zu aktivieren.", "retrieve_current_mode": "Fehler beim Abrufen des aktuellen Modus aus dem Zustand.", "failed_delete_repo": "Fehler beim Löschen des zugehörigen Shadow-Repositorys oder -Zweigs: {{error}}", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index b06f5e8091e..89b1b292fb6 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -44,7 +44,7 @@ "delete_api_config": "Failed to delete api configuration", "list_api_config": "Failed to get list api configuration", "update_server_timeout": "Failed to update server timeout", - "create_mcp_json": "Failed to create or open .roo/mcp.json: {{error}}", + "create_mcp_json": "Failed to create or open .pearai-agent/mcp.json: {{error}}", "hmr_not_running": "Local development server is not running, HMR will not work. Please run 'npm run dev' before launching the extension to enable HMR.", "retrieve_current_mode": "Error: failed to retrieve current mode from state.", "failed_delete_repo": "Failed to delete associated shadow repository or branch: {{error}}", diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 25affcb4581..4a9b82e0be9 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -45,7 +45,7 @@ "list_api_config": "Error al obtener la lista de configuraciones de API", "update_server_timeout": "Error al actualizar el tiempo de espera del servidor", "failed_update_project_mcp": "Error al actualizar los servidores MCP del proyecto", - "create_mcp_json": "Error al crear o abrir .roo/mcp.json: {{error}}", + "create_mcp_json": "Error al crear o abrir .pearai-agent/mcp.json: {{error}}", "hmr_not_running": "El servidor de desarrollo local no está en ejecución, HMR no funcionará. Por favor, ejecuta 'npm run dev' antes de lanzar la extensión para habilitar HMR.", "retrieve_current_mode": "Error al recuperar el modo actual del estado.", "failed_delete_repo": "Error al eliminar el repositorio o rama asociada: {{error}}", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index d4ed08c1911..55e172100ee 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -45,7 +45,7 @@ "list_api_config": "Erreur lors de l'obtention de la liste des configurations API", "update_server_timeout": "Erreur lors de la mise à jour du délai d'attente du serveur", "failed_update_project_mcp": "Échec de la mise à jour des serveurs MCP du projet", - "create_mcp_json": "Échec de la création ou de l'ouverture de .roo/mcp.json : {{error}}", + "create_mcp_json": "Échec de la création ou de l'ouverture de .pearai-agent/mcp.json : {{error}}", "hmr_not_running": "Le serveur de développement local n'est pas en cours d'exécution, HMR ne fonctionnera pas. Veuillez exécuter 'npm run dev' avant de lancer l'extension pour activer l'HMR.", "retrieve_current_mode": "Erreur lors de la récupération du mode actuel à partir du state.", "failed_delete_repo": "Échec de la suppression du repo fantôme ou de la branche associée : {{error}}", diff --git a/src/i18n/locales/hi/common.json b/src/i18n/locales/hi/common.json index 8c244009d18..e4f3a51ebb8 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -45,7 +45,7 @@ "list_api_config": "API कॉन्फ़िगरेशन की सूची प्राप्त करने में विफल", "update_server_timeout": "सर्वर टाइमआउट अपडेट करने में विफल", "failed_update_project_mcp": "प्रोजेक्ट MCP सर्वर अपडेट करने में विफल", - "create_mcp_json": ".roo/mcp.json बनाने या खोलने में विफल: {{error}}", + "create_mcp_json": ".pearai-agent/mcp.json बनाने या खोलने में विफल: {{error}}", "hmr_not_running": "स्थानीय विकास सर्वर चल नहीं रहा है, HMR काम नहीं करेगा। कृपया HMR सक्षम करने के लिए एक्सटेंशन लॉन्च करने से पहले 'npm run dev' चलाएँ।", "retrieve_current_mode": "स्टेट से वर्तमान मोड प्राप्त करने में त्रुटि।", "failed_delete_repo": "संबंधित शैडो रिपॉजिटरी या ब्रांच हटाने में विफल: {{error}}", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index 2c9879186f2..94dda7ea3f1 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -45,7 +45,7 @@ "list_api_config": "Errore durante l'ottenimento dell'elenco delle configurazioni API", "update_server_timeout": "Errore durante l'aggiornamento del timeout del server", "failed_update_project_mcp": "Errore durante l'aggiornamento dei server MCP del progetto", - "create_mcp_json": "Impossibile creare o aprire .roo/mcp.json: {{error}}", + "create_mcp_json": "Impossibile creare o aprire .pearai-agent/mcp.json: {{error}}", "hmr_not_running": "Il server di sviluppo locale non è in esecuzione, l'HMR non funzionerà. Esegui 'npm run dev' prima di avviare l'estensione per abilitare l'HMR.", "retrieve_current_mode": "Errore durante il recupero della modalità corrente dallo stato.", "failed_delete_repo": "Impossibile eliminare il repository o il ramo associato: {{error}}", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index a2b283bd83e..dc6a9da9299 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -45,7 +45,7 @@ "list_api_config": "API設定リストの取得に失敗しました", "update_server_timeout": "サーバータイムアウトの更新に失敗しました", "failed_update_project_mcp": "プロジェクトMCPサーバーの更新に失敗しました", - "create_mcp_json": ".roo/mcp.jsonの作成または開くことに失敗しました:{{error}}", + "create_mcp_json": ".pearai-agent/mcp.jsonの作成または開くことに失敗しました:{{error}}", "hmr_not_running": "ローカル開発サーバーが実行されていないため、HMRは機能しません。HMRを有効にするには、拡張機能を起動する前に'npm run dev'を実行してください。", "retrieve_current_mode": "現在のモードを状態から取得する際にエラーが発生しました。", "failed_delete_repo": "関連するシャドウリポジトリまたはブランチの削除に失敗しました:{{error}}", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index 9700d80f0e0..8e1ba132f44 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -45,7 +45,7 @@ "list_api_config": "API 구성 목록 가져오기에 실패했습니다", "update_server_timeout": "서버 타임아웃 업데이트에 실패했습니다", "failed_update_project_mcp": "프로젝트 MCP 서버 업데이트에 실패했습니다", - "create_mcp_json": ".roo/mcp.json 생성 또는 열기 실패: {{error}}", + "create_mcp_json": ".pearai-agent/mcp.json 생성 또는 열기 실패: {{error}}", "hmr_not_running": "로컬 개발 서버가 실행되고 있지 않아 HMR이 작동하지 않습니다. HMR을 활성화하려면 확장 프로그램을 실행하기 전에 'npm run dev'를 실행하세요.", "retrieve_current_mode": "상태에서 현재 모드를 검색하는 데 오류가 발생했습니다.", "failed_delete_repo": "관련 shadow 저장소 또는 브랜치 삭제 실패: {{error}}", diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index bcb345346d8..dbc459ad581 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -45,7 +45,7 @@ "list_api_config": "Nie udało się pobrać listy konfiguracji API", "update_server_timeout": "Nie udało się zaktualizować limitu czasu serwera", "failed_update_project_mcp": "Nie udało się zaktualizować serwerów MCP projektu", - "create_mcp_json": "Nie udało się utworzyć lub otworzyć .roo/mcp.json: {{error}}", + "create_mcp_json": "Nie udało się utworzyć lub otworzyć .pearai-agent/mcp.json: {{error}}", "hmr_not_running": "Lokalny serwer deweloperski nie jest uruchomiony, HMR nie będzie działać. Uruchom 'npm run dev' przed uruchomieniem rozszerzenia, aby włączyć HMR.", "retrieve_current_mode": "Błąd podczas pobierania bieżącego trybu ze stanu.", "failed_delete_repo": "Nie udało się usunąć powiązanego repozytorium lub gałęzi pomocniczej: {{error}}", diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index e2ade21da2d..0de62c10a54 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -49,7 +49,7 @@ "list_api_config": "Falha ao obter a lista de configurações da API", "update_server_timeout": "Falha ao atualizar o tempo limite do servidor", "failed_update_project_mcp": "Falha ao atualizar os servidores MCP do projeto", - "create_mcp_json": "Falha ao criar ou abrir .roo/mcp.json: {{error}}", + "create_mcp_json": "Falha ao criar ou abrir .pearai-agent/mcp.json: {{error}}", "hmr_not_running": "O servidor de desenvolvimento local não está em execução, o HMR não funcionará. Por favor, execute 'npm run dev' antes de iniciar a extensão para habilitar o HMR.", "retrieve_current_mode": "Erro ao recuperar o modo atual do estado.", "failed_delete_repo": "Falha ao excluir o repositório ou ramificação associada: {{error}}", diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index 9f2ee4d9e87..f9f3a87b2d0 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -44,7 +44,7 @@ "delete_api_config": "Не удалось удалить конфигурацию API", "list_api_config": "Не удалось получить список конфигураций API", "update_server_timeout": "Не удалось обновить таймаут сервера", - "create_mcp_json": "Не удалось создать или открыть .roo/mcp.json: {{error}}", + "create_mcp_json": "Не удалось создать или открыть .pearai-agent/mcp.json: {{error}}", "hmr_not_running": "Локальный сервер разработки не запущен, HMR не будет работать. Пожалуйста, запустите 'npm run dev' перед запуском расширения для включения HMR.", "retrieve_current_mode": "Ошибка: не удалось получить текущий режим из состояния.", "failed_delete_repo": "Не удалось удалить связанный теневой репозиторий или ветку: {{error}}", diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 9991cf0f825..0b3045166c5 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -45,7 +45,7 @@ "list_api_config": "API yapılandırma listesi alınamadı", "update_server_timeout": "Sunucu zaman aşımı güncellenemedi", "failed_update_project_mcp": "Proje MCP sunucuları güncellenemedi", - "create_mcp_json": ".roo/mcp.json oluşturulamadı veya açılamadı: {{error}}", + "create_mcp_json": ".pearai-agent/mcp.json oluşturulamadı veya açılamadı: {{error}}", "hmr_not_running": "Yerel geliştirme sunucusu çalışmıyor, HMR çalışmayacak. HMR'yi etkinleştirmek için uzantıyı başlatmadan önce lütfen 'npm run dev' komutunu çalıştırın.", "retrieve_current_mode": "Mevcut mod durumdan alınırken hata oluştu.", "failed_delete_repo": "İlişkili gölge depo veya dal silinemedi: {{error}}", diff --git a/src/i18n/locales/vi/common.json b/src/i18n/locales/vi/common.json index 613c62c8b3e..1375111ed95 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -45,7 +45,7 @@ "list_api_config": "Không thể lấy danh sách cấu hình API", "update_server_timeout": "Không thể cập nhật thời gian chờ máy chủ", "failed_update_project_mcp": "Không thể cập nhật máy chủ MCP của dự án", - "create_mcp_json": "Không thể tạo hoặc mở .roo/mcp.json: {{error}}", + "create_mcp_json": "Không thể tạo hoặc mở .pearai-agent/mcp.json: {{error}}", "hmr_not_running": "Máy chủ phát triển cục bộ không chạy, HMR sẽ không hoạt động. Vui lòng chạy 'npm run dev' trước khi khởi chạy tiện ích mở rộng để bật HMR.", "retrieve_current_mode": "Lỗi không thể truy xuất chế độ hiện tại từ trạng thái.", "failed_delete_repo": "Không thể xóa kho lưu trữ hoặc nhánh liên quan: {{error}}", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index 9e4512c584d..66e84d55e07 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -45,7 +45,7 @@ "list_api_config": "获取API配置列表失败", "update_server_timeout": "更新服务器超时设置失败", "failed_update_project_mcp": "更新项目MCP服务器失败", - "create_mcp_json": "创建或打开 .roo/mcp.json 失败:{{error}}", + "create_mcp_json": "创建或打开 .pearai-agent/mcp.json 失败:{{error}}", "hmr_not_running": "本地开发服务器未运行,HMR将不起作用。请在启动扩展前运行'npm run dev'以启用HMR。", "retrieve_current_mode": "从状态中检索当前模式失败。", "failed_delete_repo": "删除关联的影子仓库或分支失败:{{error}}", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index 1d12b862907..61f01c30622 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -45,7 +45,7 @@ "list_api_config": "取得 API 設定列表失敗", "update_server_timeout": "更新伺服器超時設定失敗", "failed_update_project_mcp": "更新專案 MCP 伺服器失敗", - "create_mcp_json": "建立或開啟 .roo/mcp.json 失敗:{{error}}", + "create_mcp_json": "建立或開啟 .pearai-agent/mcp.json 失敗:{{error}}", "hmr_not_running": "本機開發伺服器沒有執行,HMR 將不起作用。請在啟動擴充套件前執行'npm run dev'以啟用 HMR。", "retrieve_current_mode": "從狀態中檢索目前模式失敗。", "failed_delete_repo": "刪除關聯的影子倉庫或分支失敗:{{error}}", diff --git a/src/schemas/index.ts b/src/schemas/index.ts index db61b52a2e2..9b8175053b8 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -149,22 +149,6 @@ export type ApiConfigMeta = z.infer * HistoryItem */ -export const historyItemSchema = z.object({ - id: z.string(), - number: z.number(), - ts: z.number(), - task: z.string(), - tokensIn: z.number(), - tokensOut: z.number(), - cacheWrites: z.number().optional(), - cacheReads: z.number().optional(), - totalCost: z.number(), - size: z.number().optional(), - workspace: z.string().optional(), -}) - -export type HistoryItem = z.infer - /** * GroupOptions */ @@ -231,6 +215,7 @@ export const modeConfigSchema = z.object({ customInstructions: z.string().optional(), groups: groupEntryArraySchema, source: z.enum(["global", "project"]).optional(), + backendOnly: z.boolean().optional() }) export type ModeConfig = z.infer @@ -339,6 +324,35 @@ export type Experiments = z.infer type _AssertExperiments = AssertEqual>> +export const creatorModeConfigSchema = z.object({ + creatorMode: z.boolean().optional(), + newProjectType: z.string().optional(), + newProjectPath: z.string().optional(), +}); + +export type CreatorModeConfig = z.infer + +/** + * HistoryItem + */ + +export const historyItemSchema = z.object({ + id: z.string(), + number: z.number(), + ts: z.number(), + task: z.string(), + tokensIn: z.number(), + tokensOut: z.number(), + cacheWrites: z.number().optional(), + cacheReads: z.number().optional(), + totalCost: z.number(), + size: z.number().optional(), + workspace: z.string().optional(), + creatorModeConfig: creatorModeConfigSchema.optional(), +}) + +export type HistoryItem = z.infer + /** * ProviderSettings */ @@ -449,6 +463,7 @@ export const providerSettingsSchema = z.object({ defaultModelId: z.string().optional(), }) .optional(), + creatorModeConfig: creatorModeConfigSchema.optional(), }) export type ProviderSettings = z.infer @@ -546,6 +561,7 @@ const providerSettingsRecord: ProviderSettingsRecord = { pearaiApiKey: undefined, pearaiModelInfo: undefined, pearaiAgentModels: undefined, + creatorModeConfig: undefined, // X.AI (Grok) xaiApiKey: undefined, } diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index e19dc849dd3..bb6ab004b3a 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -32,6 +32,7 @@ import { fileExistsAtPath } from "../../utils/fs" import { arePathsEqual } from "../../utils/path" import { injectEnv } from "../../utils/config" import { PEARAI_URL } from "../../shared/pearaiApi" +import { AGENT_RULES_DIR } from "../../shared/constants" export type McpConnection = { server: McpServer @@ -498,7 +499,7 @@ export class McpHub { } const workspaceFolder = vscode.workspace.workspaceFolders[0] - const projectMcpDir = path.join(workspaceFolder.uri.fsPath, ".roo") + const projectMcpDir = path.join(workspaceFolder.uri.fsPath, AGENT_RULES_DIR) const projectMcpPath = path.join(projectMcpDir, "mcp.json") try { diff --git a/src/services/telemetry/PostHogClient.ts b/src/services/telemetry/PostHogClient.ts index 784c9476e8b..31a7fb70bd7 100644 --- a/src/services/telemetry/PostHogClient.ts +++ b/src/services/telemetry/PostHogClient.ts @@ -2,6 +2,7 @@ import { PostHog } from "posthog-node" import * as vscode from "vscode" import { logger } from "../../utils/logging" +import { getpearAIExports } from "../../activate/registerPearListener" // This forward declaration is needed to avoid circular dependencies export interface ClineProviderInterface { @@ -38,13 +39,42 @@ export class PostHogClient { } private static instance: PostHogClient - private client: PostHog - private distinctId: string = vscode.env.machineId - private telemetryEnabled: boolean = false - private providerRef: WeakRef | null = null + private readonly client: PostHog + private readonly vscMachineId: string + private pearaiId: string + private telemetryEnabled: boolean + private providerRef: WeakRef | null private constructor() { - this.client = new PostHog(process.env.POSTHOG_API_KEY || "", { host: "https://us.i.posthog.com" }) + this.vscMachineId = vscode.env.machineId + this.pearaiId = this.vscMachineId // Initialize with machine ID as fallback + this.telemetryEnabled = true + this.providerRef = null + this.client = new PostHog('phc_RRjQ4roADRjH6xMbXDUDTA9WLeM5ePPvAJK19w3yj0z', { host: "https://us.i.posthog.com" }) + + // getting the pearai id from the submodule + void this.initializePearAIId() + } + + private async initializePearAIId(): Promise { + try { + const exports = await getpearAIExports() + if (exports) { + this.pearaiId = await exports.pearAPI.getUserId() + this.client.identify({ + distinctId: this.vscMachineId, + properties: { + pearAiId: this.pearaiId, + } + }); + this.client.alias({ + distinctId: this.vscMachineId, + alias: this.pearaiId, + }) + } + } catch (error) { + logger.debug("Failed to get PearAI exports, using machine ID as fallback") + } } /** @@ -53,16 +83,16 @@ export class PostHogClient { * @param didUserOptIn Whether the user has explicitly opted into telemetry */ public updateTelemetryState(didUserOptIn: boolean): void { - this.telemetryEnabled = false + this.telemetryEnabled = true // First check global telemetry level - telemetry should only be enabled when level is "all" - const telemetryLevel = vscode.workspace.getConfiguration("telemetry").get("telemetryLevel", "all") - const globalTelemetryEnabled = telemetryLevel === "all" + // const telemetryLevel = vscode.workspace.getConfiguration("telemetry").get("telemetryLevel", "all") + // const globalTelemetryEnabled = telemetryLevel === "all" // We only enable telemetry if global vscode telemetry is enabled - if (globalTelemetryEnabled) { - this.telemetryEnabled = didUserOptIn - } + // if (globalTelemetryEnabled) { + // this.telemetryEnabled = didUserOptIn + // } // Update PostHog client state based on telemetry preference if (this.telemetryEnabled) { @@ -124,7 +154,7 @@ export class PostHogClient { } this.client.capture({ - distinctId: this.distinctId, + distinctId: this.vscMachineId, event: event.event, properties: mergedProperties, }) diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index ec686e5e7e7..e6f0c1297c8 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -68,6 +68,7 @@ export interface ExtensionMessage { | "acceptInput" | "setHistoryPreviewCollapsed" | "commandExecutionStatus" + | "creatorModeUpdate" text?: string action?: | "chatButtonClicked" @@ -182,7 +183,7 @@ export type ExtensionState = Pick< enableCheckpoints: boolean maxOpenTabsContext: number // Maximum number of VSCode open tabs to include in context (0-500) maxWorkspaceFiles: number // Maximum number of files to include in current working directory details (0-500) - showRooIgnoredFiles: boolean // Whether to show .rooignore'd files in listings + showRooIgnoredFiles: boolean // Whether to show .pearai-agent-ignore'd files in listings maxReadFileLine: number // Maximum number of lines to read from a file before truncating experiments: Record // Map of experiment IDs to their enabled state @@ -202,6 +203,11 @@ export type ExtensionState = Pick< renderContext: "sidebar" | "editor" settingsImportedAt?: number historyPreviewCollapsed?: boolean + creatorModeConfig?: { + creatorMode?: boolean + newProjectType?: string + newProjectPath?: string + } } export type { ClineMessage, ClineAsk, ClineSay } diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index d26a20ee3d9..60bd0a5daa9 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -128,6 +128,7 @@ export interface WebviewMessage { | "toggleApiConfigPin" | "setHistoryPreviewCollapsed" | "openPearAIAuth" + | "openPearAICreatorFeedbackOverlay" text?: string disabled?: boolean askResponse?: ClineAskResponse diff --git a/src/shared/constants.ts b/src/shared/constants.ts new file mode 100644 index 00000000000..ad3b74bf628 --- /dev/null +++ b/src/shared/constants.ts @@ -0,0 +1,5 @@ +export const AGENT_RULES_DIR = ".pearai-agent" + +export const AGENT_IGNORE_FILE_NAME = ".pearai-agent-ignore" + +export const AGENT_MODES_FILE_NAME = ".pearai-agent-modes" diff --git a/src/shared/creatorMode.ts b/src/shared/creatorMode.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/shared/modes.ts b/src/shared/modes.ts index 257f4bcf946..d109c872b3d 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -1,9 +1,10 @@ import * as vscode from "vscode" - import { GroupOptions, GroupEntry, ModeConfig, PromptComponent, CustomModePrompts, ExperimentId } from "../schemas" import { TOOL_GROUPS, ToolGroup, ALWAYS_AVAILABLE_TOOLS } from "./tools" import { addCustomInstructions } from "../core/prompts/sections/custom-instructions" import { EXPERIMENT_IDS } from "./experiments" + +// Mode types export type Mode = string export type { GroupOptions, GroupEntry, ModeConfig, PromptComponent, CustomModePrompts } @@ -50,8 +51,19 @@ export function getToolsForMode(groups: readonly GroupEntry[]): string[] { return Array.from(tools) } +export const PEARAI_CREATOR_MODE_WEBAPP_MANAGER_SLUG = 'pearai-creator-webapp-installer' as const; + // Main modes configuration as an ordered array export const modes: readonly ModeConfig[] = [ + { + slug: PEARAI_CREATOR_MODE_WEBAPP_MANAGER_SLUG, + name: "🍐 PearAI Creator Webapp Installer", + roleDefinition: "You are a PearAI Creator Webapp Manager", + customInstructions: + "", + groups: ["read", "edit", "browser", "command", "mcp"], + backendOnly: true, + }, { slug: "code", name: "💻 Code", diff --git a/src/utils/path.ts b/src/utils/path.ts index a58d6301725..296665d907d 100644 --- a/src/utils/path.ts +++ b/src/utils/path.ts @@ -2,6 +2,7 @@ import * as path from "path" import os from "os" import * as vscode from "vscode" + /* The Node.js 'path' module resolves and normalizes paths differently depending on the platform: - On Windows, it uses backslashes (\) as the default path separator. diff --git a/src/utils/util.ts b/src/utils/util.ts new file mode 100644 index 00000000000..54e5cbc8f04 --- /dev/null +++ b/src/utils/util.ts @@ -0,0 +1,19 @@ +class AssertionError extends Error { + constructor(message: string) { + super(message) + // Adding the stack info to error. + // Inspired by: https://blog.dennisokeeffe.com/blog/2020-08-07-error-tracing-with-sentry-and-es6-classes + if (Error.captureStackTrace) { + Error.captureStackTrace(this, AssertionError) + } else { + this.stack = new Error(message).stack + } + this.name = "AssertionError" + } +} + +export function assert(condition: boolean, message: string): asserts condition { + if (!condition) { + throw new AssertionError(message) + } +} diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index c2b07ba65ef..d0d15af855c 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@headlessui/react": "^2.2.0", "@heroicons/react": "^2.2.0", + "@pearai/core": "file:./../../pearai-submodule/core", "@radix-ui/react-alert-dialog": "^1.1.6", "@radix-ui/react-checkbox": "^1.1.5", "@radix-ui/react-collapsible": "^1.1.3", @@ -92,6 +93,94 @@ "vite": "6.0.11" } }, + "../../pearai-submodule/core": { + "name": "@continuedev/core", + "version": "1.0.13", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-bedrock-runtime": "^3.620.1", + "@aws-sdk/credential-providers": "^3.620.1", + "@continuedev/config-types": "^1.0.10", + "@continuedev/llm-info": "^1.0.1", + "@mozilla/readability": "^0.5.0", + "@octokit/rest": "^20.0.2", + "@supabase/supabase-js": "^2.45.1", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", + "@xenova/transformers": "2.14.0", + "adf-to-md": "^1.1.0", + "async-mutex": "^0.5.0", + "axios": "^1.6.7", + "cheerio": "^1.0.0-rc.12", + "commander": "^12.0.0", + "comment-json": "^4.2.3", + "dbinfoz": "^0.1.4", + "dotenv": "^16.4.5", + "fastest-levenshtein": "^1.0.16", + "follow-redirects": "^1.15.5", + "handlebars": "^4.7.8", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "ignore": "^5.3.1", + "js-tiktoken": "^1.0.8", + "jsdom": "^24.0.0", + "jwt-decode": "^4.0.0", + "launchdarkly-node-client-sdk": "^3.2.0", + "llm-code-highlighter": "^0.0.14", + "mac-ca": "^3.1.0", + "node-fetch": "^3.3.2", + "node-html-markdown": "^1.3.0", + "ollama": "^0.4.6", + "onnxruntime-node": "1.14.0", + "openai": "^4.20.1", + "pg": "^8.11.3", + "posthog-node": "^3.6.3", + "quick-lru": "^7.0.0", + "replicate": "^0.26.0", + "request": "^2.88.2", + "socket.io-client": "^4.7.3", + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7", + "system-ca": "^1.0.3", + "tree-sitter-wasms": "^0.1.11", + "uuid": "^9.0.1", + "vectordb": "^0.4.20", + "web-tree-sitter": "^0.21.0", + "win-ca": "^3.5.1", + "workerpool": "^9.1.3", + "yaml": "^2.4.2", + "zod": "^3.23.8" + }, + "devDependencies": { + "@babel/preset-env": "^7.24.7", + "@biomejs/biome": "1.6.4", + "@google/generative-ai": "^0.11.4", + "@types/follow-redirects": "^1.14.4", + "@types/jest": "^29.5.12", + "@types/jquery": "^3.5.29", + "@types/jsdom": "^21.1.6", + "@types/mozilla-readability": "^0.2.1", + "@types/mustache": "^4.2.5", + "@types/node-fetch": "^2.6.11", + "@types/pg": "^8.11.6", + "@types/request": "^2.48.12", + "@types/uuid": "^9.0.7", + "@types/win-ca": "^3.5.4", + "cross-env": "^7.0.3", + "esbuild": "^0.17.19", + "eslint": "^8", + "eslint-plugin-import": "^2.29.1", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "myers-diff": "^2.1.0", + "onnxruntime-common": "1.14.0", + "onnxruntime-web": "1.14.0", + "ts-jest": "^29.1.1" + }, + "engines": { + "node": ">=20.11.0" + } + }, "node_modules/@adobe/css-tools": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.1.tgz", @@ -3807,6 +3896,10 @@ "node": ">= 8" } }, + "node_modules/@pearai/core": { + "resolved": "../../pearai-submodule/core", + "link": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", diff --git a/webview-ui/package.json b/webview-ui/package.json index 0a1a7bfaed6..69adac66f32 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -17,6 +17,7 @@ "clean": "rimraf build" }, "dependencies": { + "@pearai/core": "file:./../../pearai-submodule/core", "@headlessui/react": "^2.2.0", "@heroicons/react": "^2.2.0", "@radix-ui/react-alert-dialog": "^1.1.6", diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 756579994bf..ec02f3d337a 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -813,14 +813,14 @@ const ChatTextArea = forwardRef( "flex", "flex-col", "gap-2", - "m-[10px_15px]", + // "m-[10px_15px]", "p-2", "outline-none", "border-none", - "w-[calc(100%-30px)]", + // "w-[calc(100%-30px)]", "rounded-xl", - "ml-auto", - "mr-auto", + // "ml-auto", + // "mr-auto", "box-border", textAreaDisabled ? "opacity-50" : "opacity-100", )} @@ -1039,6 +1039,7 @@ const ChatTextArea = forwardRef( value: mode.slug, label: mode.name, type: DropdownOptionType.ITEM, + backendOnly: mode.backendOnly })), { value: "sep-1", diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 33eb1916aa3..1844d74fb26 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -49,6 +49,7 @@ import { vscInputBorder, vscSidebarBorder, } from "../ui" +import { PlanningBar } from "./PlanningBar" import SystemPromptWarning from "./SystemPromptWarning" import { usePearAIModels } from "@/hooks/usePearAIModels" @@ -96,7 +97,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction { - // if last message is an ask, show user ask UI - // if user finished a task, then start a new task with a new conversation history since in this moment that the extension is waiting for user response, the user could close the extension and the conversation history would be lost. - // basically as long as a task is active, the conversation history will be persisted - if (lastMessage) { - switch (lastMessage.type) { - case "ask": - const isPartial = lastMessage.partial === true - switch (lastMessage.ask) { - case "api_req_failed": - playSound("progress_loop") - setTextAreaDisabled(true) - setClineAsk("api_req_failed") - setEnableButtons(true) - setPrimaryButtonText(t("chat:retry.title")) - setSecondaryButtonText(t("chat:startNewTask.title")) - break - case "mistake_limit_reached": - playSound("progress_loop") - setTextAreaDisabled(false) - setClineAsk("mistake_limit_reached") - setEnableButtons(true) - setPrimaryButtonText(t("chat:proceedAnyways.title")) - setSecondaryButtonText(t("chat:startNewTask.title")) - break - case "followup": - if (!isPartial) { - playSound("notification") - } - setTextAreaDisabled(isPartial) - setClineAsk("followup") - // setting enable buttons to `false` would trigger a focus grab when - // the text area is enabled which is undesirable. - // We have no buttons for this tool, so no problem having them "enabled" - // to workaround this issue. See #1358. - setEnableButtons(true) - setPrimaryButtonText(undefined) - setSecondaryButtonText(undefined) - break - case "tool": - if (!isAutoApproved(lastMessage) && !isPartial) { - playSound("notification") - } - setTextAreaDisabled(isPartial) - setClineAsk("tool") - setEnableButtons(!isPartial) - const tool = JSON.parse(lastMessage.text || "{}") as ClineSayTool - switch (tool.tool) { - case "editedExistingFile": - case "appliedDiff": - case "newFileCreated": - case "insertContent": - setPrimaryButtonText(t("chat:save.title")) - setSecondaryButtonText(t("chat:reject.title")) - break - case "finishTask": - setPrimaryButtonText(t("chat:completeSubtaskAndReturn")) - setSecondaryButtonText(undefined) - break - default: - setPrimaryButtonText(t("chat:approve.title")) - setSecondaryButtonText(t("chat:reject.title")) - break - } - break - case "browser_action_launch": - if (!isAutoApproved(lastMessage) && !isPartial) { - playSound("notification") - } - setTextAreaDisabled(isPartial) - setClineAsk("browser_action_launch") - setEnableButtons(!isPartial) - setPrimaryButtonText(t("chat:approve.title")) - setSecondaryButtonText(t("chat:reject.title")) - break - case "command": - if (!isAutoApproved(lastMessage) && !isPartial) { - playSound("notification") - } - setTextAreaDisabled(isPartial) - setClineAsk("command") - setEnableButtons(!isPartial) - setPrimaryButtonText(t("chat:runCommand.title")) - setSecondaryButtonText(t("chat:reject.title")) - break - case "command_output": - setTextAreaDisabled(false) - setClineAsk("command_output") - setEnableButtons(true) - setPrimaryButtonText(t("chat:proceedWhileRunning.title")) - setSecondaryButtonText(t("chat:killCommand.title")) - break - case "use_mcp_server": - if (!isAutoApproved(lastMessage) && !isPartial) { - playSound("notification") - } - setTextAreaDisabled(isPartial) - setClineAsk("use_mcp_server") - setEnableButtons(!isPartial) - setPrimaryButtonText(t("chat:approve.title")) - setSecondaryButtonText(t("chat:reject.title")) - break - case "completion_result": - // extension waiting for feedback. but we can just present a new task button - if (!isPartial) { - playSound("celebration") - } - setTextAreaDisabled(isPartial) - setClineAsk("completion_result") - setEnableButtons(!isPartial) - setPrimaryButtonText(t("chat:startNewTask.title")) - setSecondaryButtonText(undefined) - break - case "resume_task": - if (!isAutoApproved(lastMessage) && !isPartial) { - playSound("notification") - } - setTextAreaDisabled(false) - setClineAsk("resume_task") - setEnableButtons(true) - setPrimaryButtonText(t("chat:resumeTask.title")) - setSecondaryButtonText(t("chat:terminate.title")) - setDidClickCancel(false) // special case where we reset the cancel button state - break - case "resume_completed_task": - if (!isPartial) { - playSound("celebration") - } - setTextAreaDisabled(false) - setClineAsk("resume_completed_task") - setEnableButtons(true) - setPrimaryButtonText(t("chat:startNewTask.title")) - setSecondaryButtonText(undefined) - setDidClickCancel(false) - break - } - break - case "say": - // Don't want to reset since there could be a "say" after - // an "ask" while ask is waiting for response. - switch (lastMessage.say) { - case "api_req_retry_delayed": - setTextAreaDisabled(true) - break - case "api_req_started": - if (secondLastMessage?.ask === "command_output") { - // If the last ask is a command_output, and we - // receive an api_req_started, then that means - // the command has finished and we don't need - // input from the user anymore (in every other - // case, the user has to interact with input - // field or buttons to continue, which does the - // following automatically). - setInputValue("") - setTextAreaDisabled(true) - setSelectedImages([]) - setClineAsk(undefined) - setEnableButtons(false) - } - break - case "api_req_finished": - case "error": - case "text": - case "browser_action": - case "browser_action_result": - case "command_output": - case "mcp_server_request_started": - case "mcp_server_response": - case "completion_result": - break - } - break - } - } - }, [lastMessage, secondLastMessage]) - useEffect(() => { if (messages.length === 0) { setTextAreaDisabled(false) @@ -849,6 +675,182 @@ const ChatViewComponent: React.ForwardRefRenderFunction { + // if last message is an ask, show user ask UI + // if user finished a task, then start a new task with a new conversation history since in this moment that the extension is waiting for user response, the user could close the extension and the conversation history would be lost. + // basically as long as a task is active, the conversation history will be persisted + if (lastMessage) { + switch (lastMessage.type) { + case "ask": + const isPartial = lastMessage.partial === true + switch (lastMessage.ask) { + case "api_req_failed": + playSound("progress_loop") + setTextAreaDisabled(true) + setClineAsk("api_req_failed") + setEnableButtons(true) + setPrimaryButtonText(t("chat:retry.title")) + setSecondaryButtonText(t("chat:startNewTask.title")) + break + case "mistake_limit_reached": + playSound("progress_loop") + setTextAreaDisabled(false) + setClineAsk("mistake_limit_reached") + setEnableButtons(true) + setPrimaryButtonText(t("chat:proceedAnyways.title")) + setSecondaryButtonText(t("chat:startNewTask.title")) + break + case "followup": + if (!isPartial) { + playSound("notification") + } + setTextAreaDisabled(isPartial) + setClineAsk("followup") + // setting enable buttons to `false` would trigger a focus grab when + // the text area is enabled which is undesirable. + // We have no buttons for this tool, so no problem having them "enabled" + // to workaround this issue. See #1358. + setEnableButtons(true) + setPrimaryButtonText(undefined) + setSecondaryButtonText(undefined) + break + case "tool": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setTextAreaDisabled(isPartial) + setClineAsk("tool") + setEnableButtons(!isPartial) + const tool = JSON.parse(lastMessage.text || "{}") as ClineSayTool + switch (tool.tool) { + case "editedExistingFile": + case "appliedDiff": + case "newFileCreated": + case "insertContent": + setPrimaryButtonText(t("chat:save.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + case "finishTask": + setPrimaryButtonText(t("chat:completeSubtaskAndReturn")) + setSecondaryButtonText(undefined) + break + default: + setPrimaryButtonText(t("chat:approve.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + } + break + case "browser_action_launch": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setTextAreaDisabled(isPartial) + setClineAsk("browser_action_launch") + setEnableButtons(!isPartial) + setPrimaryButtonText(t("chat:approve.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + case "command": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setTextAreaDisabled(isPartial) + setClineAsk("command") + setEnableButtons(!isPartial) + setPrimaryButtonText(t("chat:runCommand.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + case "command_output": + setTextAreaDisabled(false) + setClineAsk("command_output") + setEnableButtons(true) + setPrimaryButtonText(t("chat:proceedWhileRunning.title")) + setSecondaryButtonText(t("chat:killCommand.title")) + break + case "use_mcp_server": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setTextAreaDisabled(isPartial) + setClineAsk("use_mcp_server") + setEnableButtons(!isPartial) + setPrimaryButtonText(t("chat:approve.title")) + setSecondaryButtonText(t("chat:reject.title")) + break + case "completion_result": + // extension waiting for feedback. but we can just present a new task button + if (!isPartial) { + playSound("celebration") + } + setTextAreaDisabled(isPartial) + setClineAsk("completion_result") + setEnableButtons(!isPartial) + setPrimaryButtonText(t("chat:startNewTask.title")) + setSecondaryButtonText(undefined) + break + case "resume_task": + if (!isAutoApproved(lastMessage) && !isPartial) { + playSound("notification") + } + setTextAreaDisabled(false) + setClineAsk("resume_task") + setEnableButtons(true) + setPrimaryButtonText(t("chat:resumeTask.title")) + setSecondaryButtonText(t("chat:terminate.title")) + setDidClickCancel(false) // special case where we reset the cancel button state + break + case "resume_completed_task": + if (!isPartial) { + playSound("celebration") + } + setTextAreaDisabled(false) + setClineAsk("resume_completed_task") + setEnableButtons(true) + setPrimaryButtonText(t("chat:startNewTask.title")) + setSecondaryButtonText(undefined) + setDidClickCancel(false) + break + } + break + case "say": + // Don't want to reset since there could be a "say" after + // an "ask" while ask is waiting for response. + switch (lastMessage.say) { + case "api_req_retry_delayed": + setTextAreaDisabled(true) + break + case "api_req_started": + if (secondLastMessage?.ask === "command_output") { + // If the last ask is a command_output, and we + // receive an api_req_started, then that means + // the command has finished and we don't need + // input from the user anymore (in every other + // case, the user has to interact with input + // field or buttons to continue, which does the + // following automatically). + setInputValue("") + setTextAreaDisabled(true) + setSelectedImages([]) + setClineAsk(undefined) + setEnableButtons(false) + } + break + case "api_req_finished": + case "error": + case "text": + case "browser_action": + case "browser_action_result": + case "command_output": + case "mcp_server_request_started": + case "mcp_server_response": + case "completion_result": + break + } + break + } + } + }, [lastMessage, secondLastMessage, isAutoApproved, t]) + useEffect(() => { // This ensures the first message is not read, future user messages are // labeled as `user_feedback`. @@ -1237,6 +1239,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction + {creatorModeConfig?.creatorMode === true && ( + vscode.postMessage({ type: "cancelTask" })} + /> + )} {task ? ( <> void + className?: string +} + +export const PlanningBar: FC = ({ isStreaming, requestedPlan, stopCallback, className }) => { + return ( +
+
+
+
+
+
+ +
Creating
+
+
{requestedPlan}
+
+
+ +
+
+
+ + +
+ + + +
+ {/* */} +
+
+ ) +} diff --git a/webview-ui/src/components/chat/button/index.tsx b/webview-ui/src/components/chat/button/index.tsx new file mode 100644 index 00000000000..82025df2d12 --- /dev/null +++ b/webview-ui/src/components/chat/button/index.tsx @@ -0,0 +1,116 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" +// FROM: https://vscode.dev/github/trypear/pearai-submodule/blob/acorn/253-submodule-api-fixed/gui/src/pages/creator/ui/button/index.tsx#L1-L121 +// TODO: UI LIBRARY COMPONENT SHARING SHIZZ HERE! +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 border-none whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[#a1a1aa] disabled:pointer-events-none cursor-pointer disabled:cursor-default disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-[#18181b] text-[#fafafa] shadow hover:bg-[#27272a]", + destructive: "bg-[#ef4444] text-[#fafafa] shadow-sm hover:bg-[#dc2626]", + outline: "border border-[#e4e4e7] bg-[#ffffff] shadow-sm hover:bg-[#f4f4f5] hover:text-[#18181b]", + secondary: "bg-[#f4f4f5] text-[#18181b] hover:bg-[#e4e4e7]", + ghost: "hover:bg-[#f4f4f5] hover:text-[#18181b]", + link: "text-[#18181b] underline-offset-4 hover:underline", + }, + size: { + // default: "h-9 px-4 py-2", + default: "h-7 rounded-md px-2 text-md", + sm: "h-6 rounded-md px-2 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + toggled: { + true: "", + }, + }, + compoundVariants: [ + { + variant: "default", + toggled: true, + className: " bg-[#3030ad] text-[#0B84FF] hover:bg-[#3a3ad2]", // bg-[#27272a] text-[#fafafa] + }, + { + variant: "destructive", + toggled: true, + className: "bg-[#dc2626] text-[#fafafa]", + }, + { + variant: "outline", + toggled: true, + className: "bg-[#f4f4f5] text-[#18181b] border-[#a1a1aa]", + }, + { + variant: "secondary", + toggled: true, + // className: "bg-[#e4e4e7] text-[#18181b]" + className: "bg-[#E3EFFF] text-[#4388F8] hover:bg-[#D1E3FF]", + }, + { + variant: "ghost", + toggled: true, + className: "bg-[#f4f4f5] text-[#18181b]", + }, + { + variant: "link", + toggled: true, + className: "text-[#18181b] underline", + }, + ], + defaultVariants: { + variant: "default", + size: "default", + toggled: false, + }, + }, +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean + onToggle?: (toggled: boolean) => void +} + +const Button = React.forwardRef( + ( + { className, variant, size, toggled: initialToggled = false, asChild = false, onToggle, onClick, ...props }, + ref, + ) => { + const Comp = asChild ? Slot : "button" + const [toggled, setToggled] = React.useState(initialToggled) + + const handleClick = (event: React.MouseEvent) => { + if (onToggle) { + const newToggled = !toggled + setToggled(newToggled) + onToggle(newToggled) + } + + onClick?.(event) + } + + return ( + + ) + }, +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/webview-ui/src/components/prompts/PromptsView.tsx b/webview-ui/src/components/prompts/PromptsView.tsx index 4c84416a2c2..5d17dd04a60 100644 --- a/webview-ui/src/components/prompts/PromptsView.tsx +++ b/webview-ui/src/components/prompts/PromptsView.tsx @@ -29,6 +29,7 @@ import { Tab, TabContent, TabHeader } from "../common/Tab" import i18next from "i18next" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Trans } from "react-i18next" +import { AGENT_MODES_FILE_NAME, AGENT_RULES_DIR } from "@roo/shared/constants" // Get all available groups that should show in prompts view const availableGroups = (Object.keys(TOOL_GROUPS) as ToolGroup[]).filter((group) => !TOOL_GROUPS[group].alwaysAvailable) @@ -68,7 +69,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { const [visualMode, setVisualMode] = useState(mode) // Memoize modes to preserve array order - const modes = useMemo(() => getAllModes(customModes), [customModes]) + const modes = useMemo(() => getAllModes(customModes).filter(x => !x.backendOnly), [customModes]) const [testPrompt, setTestPrompt] = useState("") const [isEnhancing, setIsEnhancing] = useState(false) @@ -459,7 +460,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { e.preventDefault() // Prevent blur vscode.postMessage({ type: "openFile", - text: "./.roomodes", + text: `./${AGENT_MODES_FILE_NAME}`, values: { create: true, content: JSON.stringify({ customModes: [] }, null, 2), @@ -819,7 +820,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { // Open or create an empty file vscode.postMessage({ type: "openFile", - text: `./.roo/rules-${currentMode.slug}/rules.md`, + text: `./${AGENT_RULES_DIR}/rules-${currentMode.slug}/rules.md`, values: { create: true, content: "", @@ -901,7 +902,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { vscode.postMessage({ type: "openFile", - text: `./.roo/system-prompt-${currentMode.slug}`, + text: `./${AGENT_RULES_DIR}/system-prompt-${currentMode.slug}`, values: { create: true, content: "", @@ -956,7 +957,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { onClick={() => vscode.postMessage({ type: "openFile", - text: "./.roo/rules/rules.md", + text: `./${AGENT_RULES_DIR}/rules/rules.md`, values: { create: true, content: "", diff --git a/webview-ui/src/components/settings/ContextManagementSettings.tsx b/webview-ui/src/components/settings/ContextManagementSettings.tsx index 0f24316a12e..91f7d6913b8 100644 --- a/webview-ui/src/components/settings/ContextManagementSettings.tsx +++ b/webview-ui/src/components/settings/ContextManagementSettings.tsx @@ -84,11 +84,11 @@ export const ContextManagementSettings = ({ onChange={(e: any) => setCachedStateField("showRooIgnoredFiles", e.target.checked)} data-testid="show-rooignored-files-checkbox">
- {t("settings:contextManagement.rooignore.description")} + {t("settings:contextManagement.pearai-agent-ignore.description")}
diff --git a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx index 955ce619369..2de704d9fff 100644 --- a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx @@ -47,7 +47,7 @@ describe("ContextManagementSettings", () => { const workspaceFilesSlider = screen.getByTestId("workspace-files-limit-slider") expect(workspaceFilesSlider).toBeInTheDocument() - // Show .rooignore'd files + // Show .pearai-agent-ignore'd files const showRooIgnoredFilesCheckbox = screen.getByTestId("show-rooignored-files-checkbox") expect(showRooIgnoredFilesCheckbox).toBeInTheDocument() expect(screen.getByTestId("show-rooignored-files-checkbox")).not.toBeChecked() diff --git a/webview-ui/src/components/ui/select-dropdown.tsx b/webview-ui/src/components/ui/select-dropdown.tsx index 1641ec1ca7f..9137627b1cf 100644 --- a/webview-ui/src/components/ui/select-dropdown.tsx +++ b/webview-ui/src/components/ui/select-dropdown.tsx @@ -21,6 +21,7 @@ export interface DropdownOption { disabled?: boolean type?: DropdownOptionType pinned?: boolean + backendOnly?: boolean; } export interface SelectDropdownProps { @@ -157,7 +158,8 @@ export const SelectDropdown = React.memo( result.pop() } - return result + // filtering out "backendOnly" options + return result.filter(x => !x.backendOnly); }, [filteredOptions]) const handleSelect = React.useCallback( diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index c26c9b8af2c..6deabbd23e7 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -94,6 +94,16 @@ export interface ExtensionStateContextType extends ExtensionState { terminalCompressProgressBar?: boolean setTerminalCompressProgressBar: (value: boolean) => void setHistoryPreviewCollapsed: (value: boolean) => void + creatorModeConfig?: { + creatorMode?: boolean + newProjectType?: string + newProjectPath?: string + } + setCreatorModeConfig: (value: { + creatorMode?: boolean + newProjectType?: string + newProjectPath?: string + }) => void } export const ExtensionStateContext = createContext(undefined) @@ -103,6 +113,7 @@ export const mergeExtensionState = (prevState: ExtensionState, newState: Extensi customModePrompts: prevCustomModePrompts, customSupportPrompts: prevCustomSupportPrompts, experiments: prevExperiments, + creatorModeConfig: prevCreatorModeConfig, ...prevRest } = prevState @@ -111,6 +122,7 @@ export const mergeExtensionState = (prevState: ExtensionState, newState: Extensi customModePrompts: newCustomModePrompts, customSupportPrompts: newCustomSupportPrompts, experiments: newExperiments, + creatorModeConfig, ...newRest } = newState @@ -122,7 +134,7 @@ export const mergeExtensionState = (prevState: ExtensionState, newState: Extensi // Note that we completely replace the previous apiConfiguration object with // a new one since the state that is broadcast is the entire apiConfiguration // and therefore merging is not necessary. - return { ...rest, apiConfiguration, customModePrompts, customSupportPrompts, experiments } + return { ...rest, apiConfiguration, creatorModeConfig, customModePrompts, customSupportPrompts, experiments } } export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { @@ -169,7 +181,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode cwd: "", browserToolEnabled: true, telemetrySetting: "unset", - showRooIgnoredFiles: true, // Default to showing .rooignore'd files with lock symbol (current behavior). + showRooIgnoredFiles: true, // Default to showing .pearai-agent-ignore'd files with lock symbol (current behavior). renderContext: "sidebar", maxReadFileLine: 500, // Default max read file line limit pinnedApiConfigs: {}, // Empty object for pinned API configs @@ -178,6 +190,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode terminalZdotdir: false, // Default ZDOTDIR handling setting terminalCompressProgressBar: true, // Default to compress progress bar output historyPreviewCollapsed: false, // Initialize the new state (default to expanded) + creatorModeConfig: { creatorMode: false, newProjectType: "", newProjectPath: "" }, // Default creator mode config }) const [didHydrateState, setDidHydrateState] = useState(false) @@ -370,6 +383,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }), setHistoryPreviewCollapsed: (value) => setState((prevState) => ({ ...prevState, historyPreviewCollapsed: value })), // Implement the setter + setCreatorModeConfig: (value) => + setState((prevState) => ({ ...prevState, creatorModeConfig: value })), } return {children} diff --git a/webview-ui/src/i18n/locales/ca/prompts.json b/webview-ui/src/i18n/locales/ca/prompts.json index d37e8629425..0a1ffc1425f 100644 --- a/webview-ui/src/i18n/locales/ca/prompts.json +++ b/webview-ui/src/i18n/locales/ca/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Crear nou mode", "editModesConfig": "Editar configuració de modes", "editGlobalModes": "Editar modes globals", - "editProjectModes": "Editar modes de projecte (.roomodes)", + "editProjectModes": "Editar modes de projecte (.pearai-agent-ignore)", "createModeHelpText": "Feu clic a + per crear un nou mode personalitzat, o simplement demaneu a Roo al xat que en creï un per a vostè!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Instruccions personalitzades específiques del mode (opcional)", "resetToDefault": "Restablir a valors predeterminats", "description": "Afegiu directrius de comportament específiques per al mode {{modeName}}.", - "loadFromFile": "Les instruccions personalitzades específiques per al mode {{mode}} també es poden carregar des de la carpeta .roo/rules-{{slug}}/ al vostre espai de treball (.roorules-{{slug}} i .clinerules-{{slug}} estan obsolets i deixaran de funcionar aviat)." + "loadFromFile": "Les instruccions personalitzades específiques per al mode {{mode}} també es poden carregar des de la carpeta .pearai-agent/rules-{{slug}}/ al vostre espai de treball (.roorules-{{slug}} i .clinerules-{{slug}} estan obsolets i deixaran de funcionar aviat)." }, "globalCustomInstructions": { "title": "Instruccions personalitzades per a tots els modes", "description": "Aquestes instruccions s'apliquen a tots els modes. Proporcionen un conjunt bàsic de comportaments que es poden millorar amb instruccions específiques de cada mode a continuació.\nSi voleu que Roo pensi i parli en un idioma diferent al de la visualització del vostre editor ({{language}}), podeu especificar-ho aquí.", - "loadFromFile": "Les instruccions també es poden carregar des de la carpeta .roo/rules/ al vostre espai de treball (.roorules i .clinerules estan obsolets i deixaran de funcionar aviat)." + "loadFromFile": "Les instruccions també es poden carregar des de la carpeta .pearai-agent/rules/ al vostre espai de treball (.roorules i .clinerules estan obsolets i deixaran de funcionar aviat)." }, "systemPrompt": { "preview": "Previsualització del prompt del sistema", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Avançat: Sobreescriure prompt del sistema", - "description": "Podeu reemplaçar completament el prompt del sistema per a aquest mode (a part de la definició de rol i instruccions personalitzades) creant un fitxer a .roo/system-prompt-{{slug}} al vostre espai de treball. Aquesta és una funcionalitat molt avançada que eludeix les salvaguardes integrades i les comprovacions de consistència (especialment al voltant de l'ús d'eines), així que aneu amb compte!" + "description": "Podeu reemplaçar completament el prompt del sistema per a aquest mode (a part de la definició de rol i instruccions personalitzades) creant un fitxer a .pearai-agent/system-prompt-{{slug}} al vostre espai de treball. Aquesta és una funcionalitat molt avançada que eludeix les salvaguardes integrades i les comprovacions de consistència (especialment al voltant de l'ús d'eines), així que aneu amb compte!" }, "createModeDialog": { "title": "Crear nou mode", @@ -122,7 +122,7 @@ "description": "Disponible a tots els espais de treball" }, "project": { - "label": "Específic del projecte (.roomodes)", + "label": "Específic del projecte (.pearai-agent-ignore)", "description": "Només disponible en aquest espai de treball, té prioritat sobre el global" } }, diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index badeaf63bb9..b742cfeb71c 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -298,9 +298,9 @@ "label": "Límit de context de fitxers de l'espai de treball", "description": "Nombre màxim de fitxers a incloure als detalls del directori de treball actual. Valors més alts proporcionen més context però augmenten l'ús de token." }, - "rooignore": { - "label": "Mostrar fitxers .rooignore en llistes i cerques", - "description": "Quan està habilitat, els fitxers que coincideixen amb els patrons a .rooignore es mostraran en llistes amb un símbol de cadenat. Quan està deshabilitat, aquests fitxers s'ocultaran completament de les llistes de fitxers i cerques." + "pearai-agent-ignore": { + "label": "Mostrar fitxers .pearai-agent-ignore en llistes i cerques", + "description": "Quan està habilitat, els fitxers que coincideixen amb els patrons a .pearai-agent-ignore es mostraran en llistes amb un símbol de cadenat. Quan està deshabilitat, aquests fitxers s'ocultaran completament de les llistes de fitxers i cerques." }, "maxReadFile": { "label": "Llindar d'auto-truncament de lectura de fitxers", diff --git a/webview-ui/src/i18n/locales/de/prompts.json b/webview-ui/src/i18n/locales/de/prompts.json index 7d0a19ec470..df67f25aee5 100644 --- a/webview-ui/src/i18n/locales/de/prompts.json +++ b/webview-ui/src/i18n/locales/de/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Neuen Modus erstellen", "editModesConfig": "Moduskonfiguration bearbeiten", "editGlobalModes": "Globale Modi bearbeiten", - "editProjectModes": "Projektmodi bearbeiten (.roomodes)", + "editProjectModes": "Projektmodi bearbeiten (.pearai-agent-ignore)", "createModeHelpText": "Klicke auf +, um einen neuen benutzerdefinierten Modus zu erstellen, oder bitte Roo einfach im Chat, einen für dich zu erstellen!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Modusspezifische benutzerdefinierte Anweisungen (optional)", "resetToDefault": "Auf Standardwerte zurücksetzen", "description": "Fügen Sie verhaltensspezifische Richtlinien für den Modus {{modeName}} hinzu.", - "loadFromFile": "Benutzerdefinierte Anweisungen für den Modus {{mode}} können auch aus dem Ordner .roo/rules-{{slug}}/ in deinem Arbeitsbereich geladen werden (.roorules-{{slug}} und .clinerules-{{slug}} sind veraltet und werden bald nicht mehr funktionieren)." + "loadFromFile": "Benutzerdefinierte Anweisungen für den Modus {{mode}} können auch aus dem Ordner .pearai-agent/rules-{{slug}}/ in deinem Arbeitsbereich geladen werden (.roorules-{{slug}} und .clinerules-{{slug}} sind veraltet und werden bald nicht mehr funktionieren)." }, "globalCustomInstructions": { "title": "Benutzerdefinierte Anweisungen für alle Modi", "description": "Diese Anweisungen gelten für alle Modi. Sie bieten einen grundlegenden Satz von Verhaltensweisen, die durch modusspezifische Anweisungen unten erweitert werden können.\nWenn du möchtest, dass Roo in einer anderen Sprache als deiner Editor-Anzeigesprache ({{language}}) denkt und spricht, kannst du das hier angeben.", - "loadFromFile": "Anweisungen können auch aus dem Ordner .roo/rules/ in deinem Arbeitsbereich geladen werden (.roorules und .clinerules sind veraltet und werden bald nicht mehr funktionieren)." + "loadFromFile": "Anweisungen können auch aus dem Ordner .pearai-agent/rules/ in deinem Arbeitsbereich geladen werden (.roorules und .clinerules sind veraltet und werden bald nicht mehr funktionieren)." }, "systemPrompt": { "preview": "System-Prompt Vorschau", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Erweitert: System-Prompt überschreiben", - "description": "Du kannst den System-Prompt für diesen Modus vollständig ersetzen (abgesehen von der Rollendefinition und benutzerdefinierten Anweisungen), indem du eine Datei unter .roo/system-prompt-{{slug}} in deinem Arbeitsbereich erstellst. Dies ist eine sehr fortgeschrittene Funktion, die eingebaute Schutzmaßnahmen und Konsistenzprüfungen umgeht (besonders bei der Werkzeugnutzung), also sei vorsichtig!" + "description": "Du kannst den System-Prompt für diesen Modus vollständig ersetzen (abgesehen von der Rollendefinition und benutzerdefinierten Anweisungen), indem du eine Datei unter .pearai-agent/system-prompt-{{slug}} in deinem Arbeitsbereich erstellst. Dies ist eine sehr fortgeschrittene Funktion, die eingebaute Schutzmaßnahmen und Konsistenzprüfungen umgeht (besonders bei der Werkzeugnutzung), also sei vorsichtig!" }, "createModeDialog": { "title": "Neuen Modus erstellen", @@ -122,7 +122,7 @@ "description": "Verfügbar in allen Arbeitsbereichen" }, "project": { - "label": "Projektspezifisch (.roomodes)", + "label": "Projektspezifisch (.pearai-agent-ignore)", "description": "Nur in diesem Arbeitsbereich verfügbar, hat Vorrang vor global" } }, diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 901fc756174..ef31bc04a10 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -298,9 +298,9 @@ "label": "Workspace-Dateien Kontextlimit", "description": "Maximale Anzahl von Dateien, die in den Details des aktuellen Arbeitsverzeichnisses enthalten sein sollen. Höhere Werte bieten mehr Kontext, erhöhen aber den Token-Verbrauch." }, - "rooignore": { - "label": ".rooignore-Dateien in Listen und Suchen anzeigen", - "description": "Wenn aktiviert, werden Dateien, die mit Mustern in .rooignore übereinstimmen, in Listen mit einem Schlosssymbol angezeigt. Wenn deaktiviert, werden diese Dateien vollständig aus Dateilisten und Suchen ausgeblendet." + "pearai-agent-ignore": { + "label": ".pearai-agent-ignore-Dateien in Listen und Suchen anzeigen", + "description": "Wenn aktiviert, werden Dateien, die mit Mustern in .pearai-agent-ignore übereinstimmen, in Listen mit einem Schlosssymbol angezeigt. Wenn deaktiviert, werden diese Dateien vollständig aus Dateilisten und Suchen ausgeblendet." }, "maxReadFile": { "label": "Schwellenwert für automatische Dateilesekürzung", diff --git a/webview-ui/src/i18n/locales/en/prompts.json b/webview-ui/src/i18n/locales/en/prompts.json index ce567a48697..8b603c77c50 100644 --- a/webview-ui/src/i18n/locales/en/prompts.json +++ b/webview-ui/src/i18n/locales/en/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Create new mode", "editModesConfig": "Edit modes configuration", "editGlobalModes": "Edit Global Modes", - "editProjectModes": "Edit Project Modes (.roomodes)", + "editProjectModes": "Edit Project Modes (.pearai-agent-ignore)", "createModeHelpText": "Hit the + to create a new custom mode, or just ask Roo in chat to create one for you!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Mode-specific Custom Instructions (optional)", "resetToDefault": "Reset to default", "description": "Add behavioral guidelines specific to {{modeName}} mode.", - "loadFromFile": "Custom instructions specific to {{mode}} mode can also be loaded from the .roo/rules-{{slug}}/ folder in your workspace (.roorules-{{slug}} and .clinerules-{{slug}} are deprecated and will stop working soon)." + "loadFromFile": "Custom instructions specific to {{mode}} mode can also be loaded from the .pearai-agent/rules-{{slug}}/ folder in your workspace (.roorules-{{slug}} and .clinerules-{{slug}} are deprecated and will stop working soon)." }, "globalCustomInstructions": { "title": "Custom Instructions for All Modes", "description": "These instructions apply to all modes. They provide a base set of behaviors that can be enhanced by mode-specific instructions below.\nIf you would like Roo to think and speak in a different language than your editor display language ({{language}}), you can specify it here.", - "loadFromFile": "Instructions can also be loaded from the .roo/rules/ folder in your workspace (.roorules and .clinerules are deprecated and will stop working soon)." + "loadFromFile": "Instructions can also be loaded from the .pearai-agent/rules/ folder in your workspace (.roorules and .clinerules are deprecated and will stop working soon)." }, "systemPrompt": { "preview": "Preview System Prompt", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Advanced: Override System Prompt", - "description": "You can completely replace the system prompt for this mode (aside from the role definition and custom instructions) by creating a file at .roo/system-prompt-{{slug}} in your workspace. This is a very advanced feature that bypasses built-in safeguards and consistency checks (especially around tool usage), so be careful!" + "description": "You can completely replace the system prompt for this mode (aside from the role definition and custom instructions) by creating a file at .pearai-agent/system-prompt-{{slug}} in your workspace. This is a very advanced feature that bypasses built-in safeguards and consistency checks (especially around tool usage), so be careful!" }, "createModeDialog": { "title": "Create New Mode", @@ -122,7 +122,7 @@ "description": "Available in all workspaces" }, "project": { - "label": "Project-specific (.roomodes)", + "label": "Project-specific (.pearai-agent-ignore)", "description": "Only available in this workspace, takes precedence over global" } }, diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 99e4fee600d..197937bb26e 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -298,9 +298,9 @@ "label": "Workspace files context limit", "description": "Maximum number of files to include in current working directory details. Higher values provide more context but increase token usage." }, - "rooignore": { - "label": "Show .rooignore'd files in lists and searches", - "description": "When enabled, files matching patterns in .rooignore will be shown in lists with a lock symbol. When disabled, these files will be completely hidden from file lists and searches." + "pearai-agent-ignore": { + "label": "Show .pearai-agent-ignore'd files in lists and searches", + "description": "When enabled, files matching patterns in .pearai-agent-ignore will be shown in lists with a lock symbol. When disabled, these files will be completely hidden from file lists and searches." }, "maxReadFile": { "label": "File read auto-truncate threshold", diff --git a/webview-ui/src/i18n/locales/es/prompts.json b/webview-ui/src/i18n/locales/es/prompts.json index 3c6b0526393..a34f97b0deb 100644 --- a/webview-ui/src/i18n/locales/es/prompts.json +++ b/webview-ui/src/i18n/locales/es/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Crear nuevo modo", "editModesConfig": "Editar configuración de modos", "editGlobalModes": "Editar modos globales", - "editProjectModes": "Editar modos del proyecto (.roomodes)", + "editProjectModes": "Editar modos del proyecto (.pearai-agent-ignore)", "createModeHelpText": "¡Haz clic en + para crear un nuevo modo personalizado, o simplemente pídele a Roo en el chat que te cree uno!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Instrucciones personalizadas para el modo (opcional)", "resetToDefault": "Restablecer a valores predeterminados", "description": "Agrega directrices de comportamiento específicas para el modo {{modeName}}.", - "loadFromFile": "Las instrucciones personalizadas para el modo {{mode}} también se pueden cargar desde la carpeta .roo/rules-{{slug}}/ en tu espacio de trabajo (.roorules-{{slug}} y .clinerules-{{slug}} están obsoletos y dejarán de funcionar pronto)." + "loadFromFile": "Las instrucciones personalizadas para el modo {{mode}} también se pueden cargar desde la carpeta .pearai-agent/rules-{{slug}}/ en tu espacio de trabajo (.roorules-{{slug}} y .clinerules-{{slug}} están obsoletos y dejarán de funcionar pronto)." }, "globalCustomInstructions": { "title": "Instrucciones personalizadas para todos los modos", "description": "Estas instrucciones se aplican a todos los modos. Proporcionan un conjunto base de comportamientos que pueden ser mejorados por instrucciones específicas de cada modo.\nSi quieres que Roo piense y hable en un idioma diferente al idioma de visualización de tu editor ({{language}}), puedes especificarlo aquí.", - "loadFromFile": "Las instrucciones también se pueden cargar desde la carpeta .roo/rules/ en tu espacio de trabajo (.roorules y .clinerules están obsoletos y dejarán de funcionar pronto)." + "loadFromFile": "Las instrucciones también se pueden cargar desde la carpeta .pearai-agent/rules/ en tu espacio de trabajo (.roorules y .clinerules están obsoletos y dejarán de funcionar pronto)." }, "systemPrompt": { "preview": "Vista previa de la solicitud del sistema", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Avanzado: Anular solicitud del sistema", - "description": "Puedes reemplazar completamente la solicitud del sistema para este modo (aparte de la definición de rol e instrucciones personalizadas) creando un archivo en .roo/system-prompt-{{slug}} en tu espacio de trabajo. ¡Esta es una función muy avanzada que omite las salvaguardas integradas y las verificaciones de consistencia (especialmente en torno al uso de herramientas), así que ten cuidado!" + "description": "Puedes reemplazar completamente la solicitud del sistema para este modo (aparte de la definición de rol e instrucciones personalizadas) creando un archivo en .pearai-agent/system-prompt-{{slug}} en tu espacio de trabajo. ¡Esta es una función muy avanzada que omite las salvaguardas integradas y las verificaciones de consistencia (especialmente en torno al uso de herramientas), así que ten cuidado!" }, "createModeDialog": { "title": "Crear nuevo modo", @@ -122,7 +122,7 @@ "description": "Disponible en todos los espacios de trabajo" }, "project": { - "label": "Específico del proyecto (.roomodes)", + "label": "Específico del proyecto (.pearai-agent-ignore)", "description": "Solo disponible en este espacio de trabajo, tiene prioridad sobre el global" } }, diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 71630ef98e0..74b2b9fd0f6 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -298,9 +298,9 @@ "label": "Límite de contexto de archivos del espacio de trabajo", "description": "Número máximo de archivos a incluir en los detalles del directorio de trabajo actual. Valores más altos proporcionan más contexto pero aumentan el uso de token." }, - "rooignore": { - "label": "Mostrar archivos .rooignore en listas y búsquedas", - "description": "Cuando está habilitado, los archivos que coinciden con los patrones en .rooignore se mostrarán en listas con un símbolo de candado. Cuando está deshabilitado, estos archivos se ocultarán completamente de las listas de archivos y búsquedas." + "pearai-agent-ignore": { + "label": "Mostrar archivos .pearai-agent-ignore en listas y búsquedas", + "description": "Cuando está habilitado, los archivos que coinciden con los patrones en .pearai-agent-ignore se mostrarán en listas con un símbolo de candado. Cuando está deshabilitado, estos archivos se ocultarán completamente de las listas de archivos y búsquedas." }, "maxReadFile": { "label": "Umbral de auto-truncado de lectura de archivos", diff --git a/webview-ui/src/i18n/locales/fr/prompts.json b/webview-ui/src/i18n/locales/fr/prompts.json index b746bd83117..72b86999bc3 100644 --- a/webview-ui/src/i18n/locales/fr/prompts.json +++ b/webview-ui/src/i18n/locales/fr/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Créer un nouveau mode", "editModesConfig": "Modifier la configuration des modes", "editGlobalModes": "Modifier les modes globaux", - "editProjectModes": "Modifier les modes du projet (.roomodes)", + "editProjectModes": "Modifier les modes du projet (.pearai-agent-ignore)", "createModeHelpText": "Cliquez sur + pour créer un nouveau mode personnalisé, ou demandez simplement à Roo dans le chat de vous en créer un !" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Instructions personnalisées spécifiques au mode (optionnel)", "resetToDefault": "Réinitialiser aux valeurs par défaut", "description": "Ajoutez des directives comportementales spécifiques au mode {{modeName}}.", - "loadFromFile": "Les instructions personnalisées spécifiques au mode {{mode}} peuvent également être chargées depuis le dossier .roo/rules-{{slug}}/ dans votre espace de travail (.roorules-{{slug}} et .clinerules-{{slug}} sont obsolètes et cesseront de fonctionner bientôt)." + "loadFromFile": "Les instructions personnalisées spécifiques au mode {{mode}} peuvent également être chargées depuis le dossier .pearai-agent/rules-{{slug}}/ dans votre espace de travail (.roorules-{{slug}} et .clinerules-{{slug}} sont obsolètes et cesseront de fonctionner bientôt)." }, "globalCustomInstructions": { "title": "Instructions personnalisées pour tous les modes", "description": "Ces instructions s'appliquent à tous les modes. Elles fournissent un ensemble de comportements de base qui peuvent être améliorés par des instructions spécifiques au mode ci-dessous.\nSi vous souhaitez que Roo pense et parle dans une langue différente de celle de votre éditeur ({{language}}), vous pouvez le spécifier ici.", - "loadFromFile": "Les instructions peuvent également être chargées depuis le dossier .roo/rules/ dans votre espace de travail (.roorules et .clinerules sont obsolètes et cesseront de fonctionner bientôt)." + "loadFromFile": "Les instructions peuvent également être chargées depuis le dossier .pearai-agent/rules/ dans votre espace de travail (.roorules et .clinerules sont obsolètes et cesseront de fonctionner bientôt)." }, "systemPrompt": { "preview": "Aperçu du prompt système", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Avancé : Remplacer le prompt système", - "description": "Vous pouvez complètement remplacer le prompt système pour ce mode (en dehors de la définition du rôle et des instructions personnalisées) en créant un fichier à .roo/system-prompt-{{slug}} dans votre espace de travail. Il s'agit d'une fonctionnalité très avancée qui contourne les garanties intégrées et les vérifications de cohérence (notamment concernant l'utilisation des outils), alors soyez prudent !" + "description": "Vous pouvez complètement remplacer le prompt système pour ce mode (en dehors de la définition du rôle et des instructions personnalisées) en créant un fichier à .pearai-agent/system-prompt-{{slug}} dans votre espace de travail. Il s'agit d'une fonctionnalité très avancée qui contourne les garanties intégrées et les vérifications de cohérence (notamment concernant l'utilisation des outils), alors soyez prudent !" }, "createModeDialog": { "title": "Créer un nouveau mode", @@ -122,7 +122,7 @@ "description": "Disponible dans tous les espaces de travail" }, "project": { - "label": "Spécifique au projet (.roomodes)", + "label": "Spécifique au projet (.pearai-agent-ignore)", "description": "Disponible uniquement dans cet espace de travail, a priorité sur le global" } }, diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 53122381b3a..9d0930d8108 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -298,9 +298,9 @@ "label": "Limite de contexte des fichiers de l'espace de travail", "description": "Nombre maximum de fichiers à inclure dans les détails du répertoire de travail actuel. Des valeurs plus élevées fournissent plus de contexte mais augmentent l'utilisation de token." }, - "rooignore": { - "label": "Afficher les fichiers .rooignore dans les listes et recherches", - "description": "Lorsque cette option est activée, les fichiers correspondant aux modèles dans .rooignore seront affichés dans les listes avec un symbole de cadenas. Lorsqu'elle est désactivée, ces fichiers seront complètement masqués des listes de fichiers et des recherches." + "pearai-agent-ignore": { + "label": "Afficher les fichiers .pearai-agent-ignore dans les listes et recherches", + "description": "Lorsque cette option est activée, les fichiers correspondant aux modèles dans .pearai-agent-ignore seront affichés dans les listes avec un symbole de cadenas. Lorsqu'elle est désactivée, ces fichiers seront complètement masqués des listes de fichiers et des recherches." }, "maxReadFile": { "label": "Seuil d'auto-troncature de lecture de fichier", diff --git a/webview-ui/src/i18n/locales/hi/prompts.json b/webview-ui/src/i18n/locales/hi/prompts.json index d5291a577c7..83092ae6a6a 100644 --- a/webview-ui/src/i18n/locales/hi/prompts.json +++ b/webview-ui/src/i18n/locales/hi/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "नया मोड बनाएँ", "editModesConfig": "मोड कॉन्फ़िगरेशन संपादित करें", "editGlobalModes": "ग्लोबल मोड्स संपादित करें", - "editProjectModes": "प्रोजेक्ट मोड्स संपादित करें (.roomodes)", + "editProjectModes": "प्रोजेक्ट मोड्स संपादित करें (.pearai-agent-ignore)", "createModeHelpText": "नया कस्टम मोड बनाने के लिए + पर क्लिक करें, या बस चैट में Roo से आपके लिए एक बनाने को कहें!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "मोड-विशिष्ट कस्टम निर्देश (वैकल्पिक)", "resetToDefault": "डिफ़ॉल्ट पर रीसेट करें", "description": "{{modeName}} मोड के लिए विशिष्ट व्यवहार दिशानिर्देश जोड़ें।", - "loadFromFile": "{{mode}} मोड के लिए विशिष्ट कस्टम निर्देश आपके वर्कस्पेस में .roo/rules-{{slug}}/ फ़ोल्डर से भी लोड किए जा सकते हैं (.roorules-{{slug}} और .clinerules-{{slug}} पुराने हो गए हैं और जल्द ही काम करना बंद कर देंगे)।" + "loadFromFile": "{{mode}} मोड के लिए विशिष्ट कस्टम निर्देश आपके वर्कस्पेस में .pearai-agent/rules-{{slug}}/ फ़ोल्डर से भी लोड किए जा सकते हैं (.roorules-{{slug}} और .clinerules-{{slug}} पुराने हो गए हैं और जल्द ही काम करना बंद कर देंगे)।" }, "globalCustomInstructions": { "title": "सभी मोड्स के लिए कस्टम निर्देश", "description": "ये निर्देश सभी मोड्स पर लागू होते हैं। वे व्यवहारों का एक आधार सेट प्रदान करते हैं जिन्हें नीचे दिए गए मोड-विशिष्ट निर्देशों द्वारा बढ़ाया जा सकता है।\nयदि आप चाहते हैं कि Roo आपके एडिटर की प्रदर्शन भाषा ({{language}}) से अलग भाषा में सोचे और बोले, तो आप यहां इसे निर्दिष्ट कर सकते हैं।", - "loadFromFile": "निर्देश आपके वर्कस्पेस में .roo/rules/ फ़ोल्डर से भी लोड किए जा सकते हैं (.roorules और .clinerules पुराने हो गए हैं और जल्द ही काम करना बंद कर देंगे)।" + "loadFromFile": "निर्देश आपके वर्कस्पेस में .pearai-agent/rules/ फ़ोल्डर से भी लोड किए जा सकते हैं (.roorules और .clinerules पुराने हो गए हैं और जल्द ही काम करना बंद कर देंगे)।" }, "systemPrompt": { "preview": "सिस्टम प्रॉम्प्ट का पूर्वावलोकन", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "उन्नत: सिस्टम प्रॉम्प्ट ओवरराइड करें", - "description": "आप अपने वर्कस्पेस में .roo/system-prompt-{{slug}} पर एक फाइल बनाकर इस मोड के लिए सिस्टम प्रॉम्प्ट को पूरी तरह से बदल सकते हैं (भूमिका परिभाषा और कस्टम निर्देशों को छोड़कर)। यह एक बहुत उन्नत सुविधा है जो अंतर्निहित सुरक्षा उपायों और सामंजस्यता जांचों को बायपास करती है (विशेष रूप से टूल उपयोग के आसपास), इसलिए सावधान रहें!" + "description": "आप अपने वर्कस्पेस में .pearai-agent/system-prompt-{{slug}} पर एक फाइल बनाकर इस मोड के लिए सिस्टम प्रॉम्प्ट को पूरी तरह से बदल सकते हैं (भूमिका परिभाषा और कस्टम निर्देशों को छोड़कर)। यह एक बहुत उन्नत सुविधा है जो अंतर्निहित सुरक्षा उपायों और सामंजस्यता जांचों को बायपास करती है (विशेष रूप से टूल उपयोग के आसपास), इसलिए सावधान रहें!" }, "createModeDialog": { "title": "नया मोड बनाएँ", @@ -122,7 +122,7 @@ "description": "सभी वर्कस्पेस में उपलब्ध" }, "project": { - "label": "प्रोजेक्ट-विशिष्ट (.roomodes)", + "label": "प्रोजेक्ट-विशिष्ट (.pearai-agent-ignore)", "description": "केवल इस वर्कस्पेस में उपलब्ध, ग्लोबल पर प्राथमिकता रखता है" } }, diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 32362ae5e25..e40028cbdd8 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -298,9 +298,9 @@ "label": "वर्कस्पेस फाइल संदर्भ सीमा", "description": "वर्तमान कार्य निर्देशिका विवरण में शामिल करने के लिए फाइलों की अधिकतम संख्या। उच्च मान अधिक संदर्भ प्रदान करते हैं लेकिन token उपयोग बढ़ाते हैं।" }, - "rooignore": { - "label": "सूचियों और खोजों में .rooignore फाइलें दिखाएँ", - "description": "जब सक्षम होता है, .rooignore में पैटर्न से मेल खाने वाली फाइलें लॉक प्रतीक के साथ सूचियों में दिखाई जाएंगी। जब अक्षम होता है, ये फाइलें फाइल सूचियों और खोजों से पूरी तरह छिपा दी जाएंगी।" + "pearai-agent-ignore": { + "label": "सूचियों और खोजों में .pearai-agent-ignore फाइलें दिखाएँ", + "description": "जब सक्षम होता है, .pearai-agent-ignore में पैटर्न से मेल खाने वाली फाइलें लॉक प्रतीक के साथ सूचियों में दिखाई जाएंगी। जब अक्षम होता है, ये फाइलें फाइल सूचियों और खोजों से पूरी तरह छिपा दी जाएंगी।" }, "maxReadFile": { "label": "फ़ाइल पढ़ने का स्वचालित काटने की सीमा", diff --git a/webview-ui/src/i18n/locales/it/prompts.json b/webview-ui/src/i18n/locales/it/prompts.json index 81bb7534def..d9e4ce12966 100644 --- a/webview-ui/src/i18n/locales/it/prompts.json +++ b/webview-ui/src/i18n/locales/it/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Crea nuova modalità", "editModesConfig": "Modifica configurazione modalità", "editGlobalModes": "Modifica modalità globali", - "editProjectModes": "Modifica modalità di progetto (.roomodes)", + "editProjectModes": "Modifica modalità di progetto (.pearai-agent-ignore)", "createModeHelpText": "Clicca sul + per creare una nuova modalità personalizzata, o chiedi semplicemente a Roo nella chat di crearne una per te!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Istruzioni personalizzate specifiche per la modalità (opzionale)", "resetToDefault": "Ripristina predefiniti", "description": "Aggiungi linee guida comportamentali specifiche per la modalità {{modeName}}.", - "loadFromFile": "Le istruzioni personalizzate specifiche per la modalità {{mode}} possono essere caricate anche dalla cartella .roo/rules-{{slug}}/ nel tuo spazio di lavoro (.roorules-{{slug}} e .clinerules-{{slug}} sono obsoleti e smetteranno di funzionare presto)." + "loadFromFile": "Le istruzioni personalizzate specifiche per la modalità {{mode}} possono essere caricate anche dalla cartella .pearai-agent/rules-{{slug}}/ nel tuo spazio di lavoro (.roorules-{{slug}} e .clinerules-{{slug}} sono obsoleti e smetteranno di funzionare presto)." }, "globalCustomInstructions": { "title": "Istruzioni personalizzate per tutte le modalità", "description": "Queste istruzioni si applicano a tutte le modalità. Forniscono un insieme base di comportamenti che possono essere migliorati dalle istruzioni specifiche per modalità qui sotto.\nSe desideri che Roo pensi e parli in una lingua diversa dalla lingua di visualizzazione del tuo editor ({{language}}), puoi specificarlo qui.", - "loadFromFile": "Le istruzioni possono essere caricate anche dalla cartella .roo/rules/ nel tuo spazio di lavoro (.roorules e .clinerules sono obsoleti e smetteranno di funzionare presto)." + "loadFromFile": "Le istruzioni possono essere caricate anche dalla cartella .pearai-agent/rules/ nel tuo spazio di lavoro (.roorules e .clinerules sono obsoleti e smetteranno di funzionare presto)." }, "systemPrompt": { "preview": "Anteprima prompt di sistema", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Avanzato: Sovrascrivi prompt di sistema", - "description": "Puoi sostituire completamente il prompt di sistema per questa modalità (a parte la definizione del ruolo e le istruzioni personalizzate) creando un file in .roo/system-prompt-{{slug}} nel tuo spazio di lavoro. Questa è una funzionalità molto avanzata che bypassa le protezioni integrate e i controlli di coerenza (specialmente riguardo all'uso degli strumenti), quindi fai attenzione!" + "description": "Puoi sostituire completamente il prompt di sistema per questa modalità (a parte la definizione del ruolo e le istruzioni personalizzate) creando un file in .pearai-agent/system-prompt-{{slug}} nel tuo spazio di lavoro. Questa è una funzionalità molto avanzata che bypassa le protezioni integrate e i controlli di coerenza (specialmente riguardo all'uso degli strumenti), quindi fai attenzione!" }, "createModeDialog": { "title": "Crea nuova modalità", @@ -122,7 +122,7 @@ "description": "Disponibile in tutti gli spazi di lavoro" }, "project": { - "label": "Specifico del progetto (.roomodes)", + "label": "Specifico del progetto (.pearai-agent-ignore)", "description": "Disponibile solo in questo spazio di lavoro, ha la precedenza sul globale" } }, diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index a697e547efa..941c3beb481 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -298,9 +298,9 @@ "label": "Limite contesto file area di lavoro", "description": "Numero massimo di file da includere nei dettagli della directory di lavoro corrente. Valori più alti forniscono più contesto ma aumentano l'utilizzo di token." }, - "rooignore": { - "label": "Mostra file .rooignore negli elenchi e nelle ricerche", - "description": "Quando abilitato, i file che corrispondono ai pattern in .rooignore verranno mostrati negli elenchi con un simbolo di blocco. Quando disabilitato, questi file saranno completamente nascosti dagli elenchi di file e dalle ricerche." + "pearai-agent-ignore": { + "label": "Mostra file .pearai-agent-ignore negli elenchi e nelle ricerche", + "description": "Quando abilitato, i file che corrispondono ai pattern in .pearai-agent-ignore verranno mostrati negli elenchi con un simbolo di blocco. Quando disabilitato, questi file saranno completamente nascosti dagli elenchi di file e dalle ricerche." }, "maxReadFile": { "label": "Soglia di auto-troncamento lettura file", diff --git a/webview-ui/src/i18n/locales/ja/prompts.json b/webview-ui/src/i18n/locales/ja/prompts.json index 16f30a2ebc5..48c2a030d02 100644 --- a/webview-ui/src/i18n/locales/ja/prompts.json +++ b/webview-ui/src/i18n/locales/ja/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "新しいモードを作成", "editModesConfig": "モード設定を編集", "editGlobalModes": "グローバルモードを編集", - "editProjectModes": "プロジェクトモードを編集 (.roomodes)", + "editProjectModes": "プロジェクトモードを編集 (.pearai-agent-ignore)", "createModeHelpText": "+ をクリックして新しいカスタムモードを作成するか、チャットで Roo に作成を依頼してください!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "モード固有のカスタム指示(オプション)", "resetToDefault": "デフォルトにリセット", "description": "{{modeName}}モードに特化した行動ガイドラインを追加します。", - "loadFromFile": "{{mode}}モード固有のカスタム指示は、ワークスペースの.roo/rules-{{slug}}/フォルダからも読み込めます(.roorules-{{slug}}と.clinerules-{{slug}}は非推奨であり、まもなく機能しなくなります)。" + "loadFromFile": "{{mode}}モード固有のカスタム指示は、ワークスペースの.pearai-agent/rules-{{slug}}/フォルダからも読み込めます(.roorules-{{slug}}と.clinerules-{{slug}}は非推奨であり、まもなく機能しなくなります)。" }, "globalCustomInstructions": { "title": "すべてのモードのカスタム指示", "description": "これらの指示はすべてのモードに適用されます。モード固有の指示で強化できる基本的な動作セットを提供します。\nRooにエディタの表示言語({{language}})とは異なる言語で考えたり話したりさせたい場合は、ここで指定できます。", - "loadFromFile": "指示はワークスペースの.roo/rules/フォルダからも読み込めます(.roorules と .clinerules は非推奨であり、まもなく機能しなくなります)。" + "loadFromFile": "指示はワークスペースの.pearai-agent/rules/フォルダからも読み込めます(.roorules と .clinerules は非推奨であり、まもなく機能しなくなります)。" }, "systemPrompt": { "preview": "システムプロンプトのプレビュー", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "詳細設定:システムプロンプトの上書き", - "description": "ワークスペースの.roo/system-prompt-{{slug}}にファイルを作成することで、このモードのシステムプロンプト(役割定義とカスタム指示以外)を完全に置き換えることができます。これは組み込みの安全対策と一貫性チェック(特にツールの使用に関して)をバイパスする非常に高度な機能なので、注意して使用してください!" + "description": "ワークスペースの.pearai-agent/system-prompt-{{slug}}にファイルを作成することで、このモードのシステムプロンプト(役割定義とカスタム指示以外)を完全に置き換えることができます。これは組み込みの安全対策と一貫性チェック(特にツールの使用に関して)をバイパスする非常に高度な機能なので、注意して使用してください!" }, "createModeDialog": { "title": "新しいモードを作成", @@ -122,7 +122,7 @@ "description": "すべてのワークスペースで利用可能" }, "project": { - "label": "プロジェクト固有 (.roomodes)", + "label": "プロジェクト固有 (.pearai-agent-ignore)", "description": "このワークスペースでのみ使用可能、グローバルよりも優先" } }, diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 25b77e1eecb..e28c0f65a93 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -298,7 +298,7 @@ "label": "ワークスペースファイルコンテキスト制限", "description": "現在の作業ディレクトリの詳細に含めるファイルの最大数。高い値はより多くのコンテキストを提供しますが、token使用量が増加します。" }, - "rooignore": { + "pearai-agent-ignore": { "label": "リストと検索で.rooignoreファイルを表示", "description": "有効にすると、.rooignoreのパターンに一致するファイルがロックシンボル付きでリストに表示されます。無効にすると、これらのファイルはファイルリストや検索から完全に非表示になります。" }, diff --git a/webview-ui/src/i18n/locales/ko/prompts.json b/webview-ui/src/i18n/locales/ko/prompts.json index 5942105f055..3c20d6f7cdb 100644 --- a/webview-ui/src/i18n/locales/ko/prompts.json +++ b/webview-ui/src/i18n/locales/ko/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "새 모드 만들기", "editModesConfig": "모드 구성 편집", "editGlobalModes": "전역 모드 편집", - "editProjectModes": "프로젝트 모드 편집 (.roomodes)", + "editProjectModes": "프로젝트 모드 편집 (.pearai-agent-ignore)", "createModeHelpText": "새 커스텀 모드를 만들려면 + 버튼을 클릭하거나, 채팅에서 Roo에게 만들어달라고 요청하세요!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "모드별 사용자 지정 지침 (선택 사항)", "resetToDefault": "기본값으로 재설정", "description": "{{modeName}} 모드에 대한 특정 행동 지침을 추가하세요.", - "loadFromFile": "{{mode}} 모드에 대한 사용자 지정 지침은 작업 공간의 .roo/rules-{{slug}}/ 폴더에서도 로드할 수 있습니다(.roorules-{{slug}}와 .clinerules-{{slug}}는 더 이상 사용되지 않으며 곧 작동을 중단합니다)." + "loadFromFile": "{{mode}} 모드에 대한 사용자 지정 지침은 작업 공간의 .pearai-agent/rules-{{slug}}/ 폴더에서도 로드할 수 있습니다(.roorules-{{slug}}와 .clinerules-{{slug}}는 더 이상 사용되지 않으며 곧 작동을 중단합니다)." }, "globalCustomInstructions": { "title": "모든 모드에 대한 사용자 지정 지침", "description": "이 지침은 모든 모드에 적용됩니다. 아래의 모드별 지침으로 향상될 수 있는 기본 동작 세트를 제공합니다.\nRoo가 에디터 표시 언어({{language}})와 다른 언어로 생각하고 말하기를 원하시면, 여기에 지정할 수 있습니다.", - "loadFromFile": "지침은 작업 공간의 .roo/rules/ 폴더에서도 로드할 수 있습니다(.roorules와 .clinerules는 더 이상 사용되지 않으며 곧 작동을 중단합니다)." + "loadFromFile": "지침은 작업 공간의 .pearai-agent/rules/ 폴더에서도 로드할 수 있습니다(.roorules와 .clinerules는 더 이상 사용되지 않으며 곧 작동을 중단합니다)." }, "systemPrompt": { "preview": "시스템 프롬프트 미리보기", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "고급: 시스템 프롬프트 재정의", - "description": "작업 공간의 .roo/system-prompt-{{slug}}에 파일을 생성하여 이 모드의 시스템 프롬프트(역할 정의 및 사용자 지정 지침 제외)를 완전히 대체할 수 있습니다. 이는 내장된 안전 장치와 일관성 검사(특히 도구 사용 관련)를 우회하는 매우 고급 기능이므로 주의하세요!" + "description": "작업 공간의 .pearai-agent/system-prompt-{{slug}}에 파일을 생성하여 이 모드의 시스템 프롬프트(역할 정의 및 사용자 지정 지침 제외)를 완전히 대체할 수 있습니다. 이는 내장된 안전 장치와 일관성 검사(특히 도구 사용 관련)를 우회하는 매우 고급 기능이므로 주의하세요!" }, "createModeDialog": { "title": "새 모드 만들기", @@ -122,7 +122,7 @@ "description": "모든 작업 공간에서 사용 가능" }, "project": { - "label": "프로젝트별 (.roomodes)", + "label": "프로젝트별 (.pearai-agent-ignore)", "description": "이 작업 공간에서만 사용 가능, 전역보다 우선" } }, diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 9c6e9df01b4..8f35891bb26 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -298,8 +298,8 @@ "label": "작업 공간 파일 컨텍스트 제한", "description": "현재 작업 디렉토리 세부 정보에 포함할 파일의 최대 수. 높은 값은 더 많은 컨텍스트를 제공하지만 token 사용량이 증가합니다." }, - "rooignore": { - "label": "목록 및 검색에서 .rooignore 파일 표시", + "pearai-agent-ignore": { + "label": "목록 및 검색에서 .pearai-agent-ignore 파일 표시", "description": "활성화되면 .rooignore의 패턴과 일치하는 파일이 잠금 기호와 함께 목록에 표시됩니다. 비활성화되면 이러한 파일은 파일 목록 및 검색에서 완전히 숨겨집니다." }, "maxReadFile": { diff --git a/webview-ui/src/i18n/locales/pl/prompts.json b/webview-ui/src/i18n/locales/pl/prompts.json index ef8a5ffe083..2782e91a4f9 100644 --- a/webview-ui/src/i18n/locales/pl/prompts.json +++ b/webview-ui/src/i18n/locales/pl/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Utwórz nowy tryb", "editModesConfig": "Edytuj konfigurację trybów", "editGlobalModes": "Edytuj tryby globalne", - "editProjectModes": "Edytuj tryby projektu (.roomodes)", + "editProjectModes": "Edytuj tryby projektu (.pearai-agent-ignore)", "createModeHelpText": "Kliknij +, aby utworzyć nowy niestandardowy tryb, lub po prostu poproś Roo w czacie, aby utworzył go dla Ciebie!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Niestandardowe instrukcje dla trybu (opcjonalne)", "resetToDefault": "Przywróć domyślne", "description": "Dodaj wytyczne dotyczące zachowania specyficzne dla trybu {{modeName}}.", - "loadFromFile": "Niestandardowe instrukcje dla trybu {{modeName}} mogą być również ładowane z folderu .roo/rules-{{modeSlug}}/ w Twoim obszarze roboczym (.roorules-{{modeSlug}} i .clinerules-{{modeSlug}} są przestarzałe i wkrótce przestaną działać)." + "loadFromFile": "Niestandardowe instrukcje dla trybu {{modeName}} mogą być również ładowane z folderu .pearai-agent/rules-{{modeSlug}}/ w Twoim obszarze roboczym (.roorules-{{modeSlug}} i .clinerules-{{modeSlug}} są przestarzałe i wkrótce przestaną działać)." }, "globalCustomInstructions": { "title": "Niestandardowe instrukcje dla wszystkich trybów", "description": "Te instrukcje dotyczą wszystkich trybów. Zapewniają podstawowy zestaw zachowań, które mogą być rozszerzone przez instrukcje specyficzne dla trybów poniżej.\nJeśli chcesz, aby Roo myślał i mówił w języku innym niż język wyświetlania Twojego edytora ({{language}}), możesz to określić tutaj.", - "loadFromFile": "Instrukcje mogą być również ładowane z folderu .roo/rules/ w Twoim obszarze roboczym (.roorules i .clinerules są przestarzałe i wkrótce przestaną działać)." + "loadFromFile": "Instrukcje mogą być również ładowane z folderu .pearai-agent/rules/ w Twoim obszarze roboczym (.roorules i .clinerules są przestarzałe i wkrótce przestaną działać)." }, "systemPrompt": { "preview": "Podgląd podpowiedzi systemowej", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Zaawansowane: Zastąp podpowiedź systemową", - "description": "Możesz całkowicie zastąpić podpowiedź systemową dla tego trybu (oprócz definicji roli i niestandardowych instrukcji) poprzez utworzenie pliku w .roo/system-prompt-{{modeSlug}} w swoim obszarze roboczym. Jest to bardzo zaawansowana funkcja, która omija wbudowane zabezpieczenia i kontrole spójności (szczególnie wokół używania narzędzi), więc bądź ostrożny!" + "description": "Możesz całkowicie zastąpić podpowiedź systemową dla tego trybu (oprócz definicji roli i niestandardowych instrukcji) poprzez utworzenie pliku w .pearai-agent/system-prompt-{{modeSlug}} w swoim obszarze roboczym. Jest to bardzo zaawansowana funkcja, która omija wbudowane zabezpieczenia i kontrole spójności (szczególnie wokół używania narzędzi), więc bądź ostrożny!" }, "createModeDialog": { "title": "Utwórz nowy tryb", @@ -122,7 +122,7 @@ "description": "Dostępny we wszystkich obszarach roboczych" }, "project": { - "label": "Specyficzny dla projektu (.roomodes)", + "label": "Specyficzny dla projektu (.pearai-agent-ignore)", "description": "Dostępny tylko w tym obszarze roboczym, ma pierwszeństwo przed globalnym" } }, diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index b54ea4f8970..8cc780dd7ca 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -298,9 +298,9 @@ "label": "Limit kontekstu plików obszaru roboczego", "description": "Maksymalna liczba plików do uwzględnienia w szczegółach bieżącego katalogu roboczego. Wyższe wartości zapewniają więcej kontekstu, ale zwiększają zużycie token." }, - "rooignore": { - "label": "Pokaż pliki .rooignore na listach i w wyszukiwaniach", - "description": "Gdy włączone, pliki pasujące do wzorców w .rooignore będą pokazywane na listach z symbolem kłódki. Gdy wyłączone, te pliki będą całkowicie ukryte z list plików i wyszukiwań." + "pearai-agent-ignore": { + "label": "Pokaż pliki .pearai-agent-ignore na listach i w wyszukiwaniach", + "description": "Gdy włączone, pliki pasujące do wzorców w .pearai-agent-ignore będą pokazywane na listach z symbolem kłódki. Gdy wyłączone, te pliki będą całkowicie ukryte z list plików i wyszukiwań." }, "maxReadFile": { "label": "Próg automatycznego skracania odczytu pliku", diff --git a/webview-ui/src/i18n/locales/pt-BR/prompts.json b/webview-ui/src/i18n/locales/pt-BR/prompts.json index f255b91a27d..b18b0fa0a70 100644 --- a/webview-ui/src/i18n/locales/pt-BR/prompts.json +++ b/webview-ui/src/i18n/locales/pt-BR/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Criar novo modo", "editModesConfig": "Editar configuração de modos", "editGlobalModes": "Editar modos globais", - "editProjectModes": "Editar modos do projeto (.roomodes)", + "editProjectModes": "Editar modos do projeto (.pearai-agent-ignore)", "createModeHelpText": "Clique em + para criar um novo modo personalizado, ou simplesmente peça ao Roo no chat para criar um para você!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Instruções personalizadas específicas do modo (opcional)", "resetToDefault": "Restaurar para padrão", "description": "Adicione diretrizes comportamentais específicas para o modo {{modeName}}.", - "loadFromFile": "Instruções personalizadas específicas para o modo {{modeName}} também podem ser carregadas da pasta .roo/rules-{{modeSlug}}/ no seu espaço de trabalho (.roorules-{{modeSlug}} e .clinerules-{{modeSlug}} estão obsoletos e deixarão de funcionar em breve)." + "loadFromFile": "Instruções personalizadas específicas para o modo {{modeName}} também podem ser carregadas da pasta .pearai-agent/rules-{{modeSlug}}/ no seu espaço de trabalho (.roorules-{{modeSlug}} e .clinerules-{{modeSlug}} estão obsoletos e deixarão de funcionar em breve)." }, "globalCustomInstructions": { "title": "Instruções personalizadas para todos os modos", "description": "Estas instruções se aplicam a todos os modos. Elas fornecem um conjunto base de comportamentos que podem ser aprimorados por instruções específicas do modo abaixo.\nSe você desejar que o Roo pense e fale em um idioma diferente do idioma de exibição do seu editor ({{language}}), você pode especificá-lo aqui.", - "loadFromFile": "As instruções também podem ser carregadas da pasta .roo/rules/ no seu espaço de trabalho (.roorules e .clinerules estão obsoletos e deixarão de funcionar em breve)." + "loadFromFile": "As instruções também podem ser carregadas da pasta .pearai-agent/rules/ no seu espaço de trabalho (.roorules e .clinerules estão obsoletos e deixarão de funcionar em breve)." }, "systemPrompt": { "preview": "Visualizar prompt do sistema", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Avançado: Substituir prompt do sistema", - "description": "Você pode substituir completamente o prompt do sistema para este modo (além da definição de função e instruções personalizadas) criando um arquivo em .roo/system-prompt-{{modeSlug}} no seu espaço de trabalho. Esta é uma funcionalidade muito avançada que contorna as salvaguardas integradas e verificações de consistência (especialmente em torno do uso de ferramentas), então tenha cuidado!" + "description": "Você pode substituir completamente o prompt do sistema para este modo (além da definição de função e instruções personalizadas) criando um arquivo em .pearai-agent/system-prompt-{{modeSlug}} no seu espaço de trabalho. Esta é uma funcionalidade muito avançada que contorna as salvaguardas integradas e verificações de consistência (especialmente em torno do uso de ferramentas), então tenha cuidado!" }, "createModeDialog": { "title": "Criar novo modo", @@ -122,7 +122,7 @@ "description": "Disponível em todos os espaços de trabalho" }, "project": { - "label": "Específico do projeto (.roomodes)", + "label": "Específico do projeto (.pearai-agent-ignore)", "description": "Disponível apenas neste espaço de trabalho, tem precedência sobre o global" } }, diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 25a7d932f18..b6e0e6e491d 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -298,9 +298,9 @@ "label": "Limite de contexto de arquivos do espaço de trabalho", "description": "Número máximo de arquivos a incluir nos detalhes do diretório de trabalho atual. Valores mais altos fornecem mais contexto, mas aumentam o uso de token." }, - "rooignore": { - "label": "Mostrar arquivos .rooignore em listas e pesquisas", - "description": "Quando ativado, os arquivos que correspondem aos padrões em .rooignore serão mostrados em listas com um símbolo de cadeado. Quando desativado, esses arquivos serão completamente ocultos das listas de arquivos e pesquisas." + "pearai-agent-ignore": { + "label": "Mostrar arquivos .pearai-agent-ignore em listas e pesquisas", + "description": "Quando ativado, os arquivos que correspondem aos padrões em .pearai-agent-ignore serão mostrados em listas com um símbolo de cadeado. Quando desativado, esses arquivos serão completamente ocultos das listas de arquivos e pesquisas." }, "maxReadFile": { "label": "Limite de auto-truncamento de leitura de arquivo", diff --git a/webview-ui/src/i18n/locales/ru/prompts.json b/webview-ui/src/i18n/locales/ru/prompts.json index 0ff448bedbd..86058aca833 100644 --- a/webview-ui/src/i18n/locales/ru/prompts.json +++ b/webview-ui/src/i18n/locales/ru/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Создать новый режим", "editModesConfig": "Редактировать конфигурацию режимов", "editGlobalModes": "Редактировать глобальные режимы", - "editProjectModes": "Редактировать режимы проекта (.roomodes)", + "editProjectModes": "Редактировать режимы проекта (.pearai-agent-ignore)", "createModeHelpText": "Нажмите +, чтобы создать новый пользовательский режим, или просто попросите Roo в чате создать его для вас!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Пользовательские инструкции для режима (необязательно)", "resetToDefault": "Сбросить по умолчанию", "description": "Добавьте рекомендации по поведению, специфичные для режима {{modeName}}.", - "loadFromFile": "Пользовательские инструкции для режима {{mode}} также можно загрузить из папки .roo/rules-{{slug}}/ в вашем рабочем пространстве (.roorules-{{slug}} и .clinerules-{{slug}} устарели и скоро перестанут работать)." + "loadFromFile": "Пользовательские инструкции для режима {{mode}} также можно загрузить из папки .pearai-agent/rules-{{slug}}/ в вашем рабочем пространстве (.roorules-{{slug}} и .clinerules-{{slug}} устарели и скоро перестанут работать)." }, "globalCustomInstructions": { "title": "Пользовательские инструкции для всех режимов", "description": "Эти инструкции применяются ко всем режимам. Они задают базовое поведение, которое можно расширить с помощью инструкций ниже.\nЕсли вы хотите, чтобы Roo думал и говорил на другом языке, отличном от языка редактора ({{language}}), укажите это здесь.", - "loadFromFile": "Инструкции также можно загрузить из папки .roo/rules/ в вашем рабочем пространстве (.roorules и .clinerules устарели и скоро перестанут работать)." + "loadFromFile": "Инструкции также можно загрузить из папки .pearai-agent/rules/ в вашем рабочем пространстве (.roorules и .clinerules устарели и скоро перестанут работать)." }, "systemPrompt": { "preview": "Предпросмотр системного промпта", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Дополнительно: переопределить системный промпт", - "description": "Вы можете полностью заменить системный промпт для этого режима (кроме определения роли и пользовательских инструкций), создав файл .roo/system-prompt-{{slug}} в вашем рабочем пространстве. Это продвинутая функция, которая обходит встроенные ограничения и проверки (особенно для инструментов), поэтому будьте осторожны!" + "description": "Вы можете полностью заменить системный промпт для этого режима (кроме определения роли и пользовательских инструкций), создав файл .pearai-agent/system-prompt-{{slug}} в вашем рабочем пространстве. Это продвинутая функция, которая обходит встроенные ограничения и проверки (особенно для инструментов), поэтому будьте осторожны!" }, "createModeDialog": { "title": "Создать новый режим", @@ -122,7 +122,7 @@ "description": "Доступно во всех рабочих пространствах" }, "project": { - "label": "Для проекта (.roomodes)", + "label": "Для проекта (.pearai-agent-ignore)", "description": "Доступно только в этом рабочем пространстве, имеет приоритет над глобальными" } }, diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index caab30c872a..6f39c47d154 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -298,9 +298,9 @@ "label": "Лимит контекста файлов рабочей области", "description": "Максимальное количество файлов, включаемых в детали текущей рабочей директории. Большее значение даёт больше контекста, но увеличивает расход токенов." }, - "rooignore": { - "label": "Показывать .rooignore-файлы в списках и поиске", - "description": "Если включено, файлы, совпадающие с шаблонами в .rooignore, будут отображаться в списках с символом замка. Если выключено, такие файлы полностью скрываются из списков и поиска." + "pearai-agent-ignore": { + "label": "Показывать .pearai-agent-ignore-файлы в списках и поиске", + "description": "Если включено, файлы, совпадающие с шаблонами в .pearai-agent-ignore, будут отображаться в списках с символом замка. Если выключено, такие файлы полностью скрываются из списков и поиска." }, "maxReadFile": { "label": "Порог автообрезки при чтении файла", diff --git a/webview-ui/src/i18n/locales/tr/prompts.json b/webview-ui/src/i18n/locales/tr/prompts.json index 8d7c7b9305a..1e505dc07de 100644 --- a/webview-ui/src/i18n/locales/tr/prompts.json +++ b/webview-ui/src/i18n/locales/tr/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Yeni mod oluştur", "editModesConfig": "Mod yapılandırmasını düzenle", "editGlobalModes": "Global modları düzenle", - "editProjectModes": "Proje modlarını düzenle (.roomodes)", + "editProjectModes": "Proje modlarını düzenle (.pearai-agent-ignore)", "createModeHelpText": "Yeni bir özel mod oluşturmak için + düğmesine tıklayın veya sohbette Roo'dan sizin için bir tane oluşturmasını isteyin!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Moda özgü özel talimatlar (isteğe bağlı)", "resetToDefault": "Varsayılana sıfırla", "description": "{{modeName}} modu için özel davranış yönergeleri ekleyin.", - "loadFromFile": "{{mode}} moduna özgü özel talimatlar ayrıca çalışma alanınızdaki .roo/rules-{{slug}}/ klasöründen yüklenebilir (.roorules-{{slug}} ve .clinerules-{{slug}} kullanımdan kaldırılmıştır ve yakında çalışmayı durduracaklardır)." + "loadFromFile": "{{mode}} moduna özgü özel talimatlar ayrıca çalışma alanınızdaki .pearai-agent/rules-{{slug}}/ klasöründen yüklenebilir (.roorules-{{slug}} ve .clinerules-{{slug}} kullanımdan kaldırılmıştır ve yakında çalışmayı durduracaklardır)." }, "globalCustomInstructions": { "title": "Tüm Modlar için Özel Talimatlar", "description": "Bu talimatlar tüm modlara uygulanır. Aşağıdaki moda özgü talimatlarla geliştirilebilen temel davranış seti sağlarlar.\nRoo'nun editörünüzün görüntüleme dilinden ({{language}}) farklı bir dilde düşünmesini ve konuşmasını istiyorsanız, burada belirtebilirsiniz.", - "loadFromFile": "Talimatlar ayrıca çalışma alanınızdaki .roo/rules/ klasöründen de yüklenebilir (.roorules ve .clinerules kullanımdan kaldırılmıştır ve yakında çalışmayı durduracaklardır)." + "loadFromFile": "Talimatlar ayrıca çalışma alanınızdaki .pearai-agent/rules/ klasöründen de yüklenebilir (.roorules ve .clinerules kullanımdan kaldırılmıştır ve yakında çalışmayı durduracaklardır)." }, "systemPrompt": { "preview": "Sistem promptunu önizle", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Gelişmiş: Sistem Promptunu Geçersiz Kıl", - "description": "Çalışma alanınızda .roo/system-prompt-{{slug}} adresinde bir dosya oluşturarak bu mod için sistem istemini tamamen değiştirebilirsiniz (rol tanımı ve özel talimatlar hariç). Bu, yerleşik güvenlik önlemlerini ve tutarlılık kontrollerini (özellikle araç kullanımıyla ilgili) aşan çok gelişmiş bir özelliktir, bu yüzden dikkatli olun!" + "description": "Çalışma alanınızda .pearai-agent/system-prompt-{{slug}} adresinde bir dosya oluşturarak bu mod için sistem istemini tamamen değiştirebilirsiniz (rol tanımı ve özel talimatlar hariç). Bu, yerleşik güvenlik önlemlerini ve tutarlılık kontrollerini (özellikle araç kullanımıyla ilgili) aşan çok gelişmiş bir özelliktir, bu yüzden dikkatli olun!" }, "createModeDialog": { "title": "Yeni Mod Oluştur", @@ -122,7 +122,7 @@ "description": "Tüm çalışma alanlarında kullanılabilir" }, "project": { - "label": "Projeye özgü (.roomodes)", + "label": "Projeye özgü (.pearai-agent-ignore)", "description": "Yalnızca bu çalışma alanında kullanılabilir, globale göre önceliklidir" } }, diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 0e98d02cea3..9ed3f29db08 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -298,9 +298,9 @@ "label": "Çalışma alanı dosyaları bağlam sınırı", "description": "Mevcut çalışma dizini ayrıntılarına dahil edilecek maksimum dosya sayısı. Daha yüksek değerler daha fazla bağlam sağlar ancak token kullanımını artırır." }, - "rooignore": { - "label": "Listelerde ve aramalarda .rooignore dosyalarını göster", - "description": "Etkinleştirildiğinde, .rooignore'daki desenlerle eşleşen dosyalar kilit sembolü ile listelerde gösterilecektir. Devre dışı bırakıldığında, bu dosyalar dosya listelerinden ve aramalardan tamamen gizlenecektir." + "pearai-agent-ignore": { + "label": "Listelerde ve aramalarda .pearai-agent-ignore dosyalarını göster", + "description": "Etkinleştirildiğinde, .pearai-agent-ignore'daki desenlerle eşleşen dosyalar kilit sembolü ile listelerde gösterilecektir. Devre dışı bırakıldığında, bu dosyalar dosya listelerinden ve aramalardan tamamen gizlenecektir." }, "maxReadFile": { "label": "Dosya okuma otomatik kısaltma eşiği", diff --git a/webview-ui/src/i18n/locales/vi/prompts.json b/webview-ui/src/i18n/locales/vi/prompts.json index 4b62d3718e6..5583a8757e0 100644 --- a/webview-ui/src/i18n/locales/vi/prompts.json +++ b/webview-ui/src/i18n/locales/vi/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "Tạo chế độ mới", "editModesConfig": "Chỉnh sửa cấu hình chế độ", "editGlobalModes": "Chỉnh sửa chế độ toàn cục", - "editProjectModes": "Chỉnh sửa chế độ dự án (.roomodes)", + "editProjectModes": "Chỉnh sửa chế độ dự án (.pearai-agent-ignore)", "createModeHelpText": "Nhấn + để tạo chế độ tùy chỉnh mới, hoặc chỉ cần yêu cầu Roo trong chat tạo một chế độ cho bạn!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "Hướng dẫn tùy chỉnh dành riêng cho chế độ (tùy chọn)", "resetToDefault": "Đặt lại về mặc định", "description": "Thêm hướng dẫn hành vi dành riêng cho chế độ {{modeName}}.", - "loadFromFile": "Hướng dẫn tùy chỉnh dành riêng cho chế độ {{modeName}} cũng có thể được tải từ thư mục .roo/rules-{{modeSlug}}/ trong không gian làm việc của bạn (.roorules-{{modeSlug}} và .clinerules-{{modeSlug}} đã lỗi thời và sẽ sớm ngừng hoạt động)." + "loadFromFile": "Hướng dẫn tùy chỉnh dành riêng cho chế độ {{modeName}} cũng có thể được tải từ thư mục .pearai-agent/rules-{{modeSlug}}/ trong không gian làm việc của bạn (.roorules-{{modeSlug}} và .clinerules-{{modeSlug}} đã lỗi thời và sẽ sớm ngừng hoạt động)." }, "globalCustomInstructions": { "title": "Hướng dẫn tùy chỉnh cho tất cả các chế độ", "description": "Những hướng dẫn này áp dụng cho tất cả các chế độ. Chúng cung cấp một bộ hành vi cơ bản có thể được nâng cao bởi hướng dẫn dành riêng cho chế độ bên dưới.\nNếu bạn muốn Roo suy nghĩ và nói bằng ngôn ngữ khác với ngôn ngữ hiển thị trình soạn thảo của bạn ({{language}}), bạn có thể chỉ định ở đây.", - "loadFromFile": "Hướng dẫn cũng có thể được tải từ thư mục .roo/rules/ trong không gian làm việc của bạn (.roorules và .clinerules đã lỗi thời và sẽ sớm ngừng hoạt động)." + "loadFromFile": "Hướng dẫn cũng có thể được tải từ thư mục .pearai-agent/rules/ trong không gian làm việc của bạn (.roorules và .clinerules đã lỗi thời và sẽ sớm ngừng hoạt động)." }, "systemPrompt": { "preview": "Xem trước lời nhắc hệ thống", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "Nâng cao: Ghi đè lời nhắc hệ thống", - "description": "Bạn có thể hoàn toàn thay thế lời nhắc hệ thống cho chế độ này (ngoài định nghĩa vai trò và hướng dẫn tùy chỉnh) bằng cách tạo một tệp tại .roo/system-prompt-{{modeSlug}} trong không gian làm việc của bạn. Đây là một tính năng rất nâng cao bỏ qua các biện pháp bảo vệ và kiểm tra nhất quán tích hợp sẵn (đặc biệt là xung quanh việc sử dụng công cụ), vì vậy hãy cẩn thận!" + "description": "Bạn có thể hoàn toàn thay thế lời nhắc hệ thống cho chế độ này (ngoài định nghĩa vai trò và hướng dẫn tùy chỉnh) bằng cách tạo một tệp tại .pearai-agent/system-prompt-{{modeSlug}} trong không gian làm việc của bạn. Đây là một tính năng rất nâng cao bỏ qua các biện pháp bảo vệ và kiểm tra nhất quán tích hợp sẵn (đặc biệt là xung quanh việc sử dụng công cụ), vì vậy hãy cẩn thận!" }, "createModeDialog": { "title": "Tạo chế độ mới", @@ -122,7 +122,7 @@ "description": "Có sẵn trong tất cả các không gian làm việc" }, "project": { - "label": "Dành riêng cho dự án (.roomodes)", + "label": "Dành riêng cho dự án (.pearai-agent-ignore)", "description": "Chỉ có sẵn trong không gian làm việc này, được ưu tiên hơn toàn cục" } }, diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 28af4b9ea11..e536476f6c7 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -298,9 +298,9 @@ "label": "Giới hạn ngữ cảnh tệp workspace", "description": "Số lượng tệp tối đa để đưa vào chi tiết thư mục làm việc hiện tại. Giá trị cao hơn cung cấp nhiều ngữ cảnh hơn nhưng tăng sử dụng token." }, - "rooignore": { - "label": "Hiển thị tệp .rooignore trong danh sách và tìm kiếm", - "description": "Khi được bật, các tệp khớp với mẫu trong .rooignore sẽ được hiển thị trong danh sách với biểu tượng khóa. Khi bị tắt, các tệp này sẽ hoàn toàn bị ẩn khỏi danh sách tệp và tìm kiếm." + "pearai-agent-ignore": { + "label": "Hiển thị tệp .pearai-agent-ignore trong danh sách và tìm kiếm", + "description": "Khi được bật, các tệp khớp với mẫu trong .pearai-agent-ignore sẽ được hiển thị trong danh sách với biểu tượng khóa. Khi bị tắt, các tệp này sẽ hoàn toàn bị ẩn khỏi danh sách tệp và tìm kiếm." }, "maxReadFile": { "label": "Ngưỡng tự động cắt ngắn khi đọc tệp", diff --git a/webview-ui/src/i18n/locales/zh-CN/prompts.json b/webview-ui/src/i18n/locales/zh-CN/prompts.json index 2980bd66997..9c8dbb504d0 100644 --- a/webview-ui/src/i18n/locales/zh-CN/prompts.json +++ b/webview-ui/src/i18n/locales/zh-CN/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "新建模式", "editModesConfig": "模式设置", "editGlobalModes": "修改全局模式", - "editProjectModes": "编辑项目模式 (.roomodes)", + "editProjectModes": "编辑项目模式 (.pearai-agent-ignore)", "createModeHelpText": "点击 + 创建模式,或在对话时让Roo创建一个新模式。" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "模式专属规则(可选)", "resetToDefault": "重置为默认值", "description": "{{modeName}}模式的专属规则", - "loadFromFile": "支持从.roo/rules-{{slug}}/目录读取配置(.roorules-{{slug}}和.clinerules-{{slug}}已弃用并将很快停止工作)。" + "loadFromFile": "支持从.pearai-agent/rules-{{slug}}/目录读取配置(.roorules-{{slug}}和.clinerules-{{slug}}已弃用并将很快停止工作)。" }, "globalCustomInstructions": { "title": "所有模式的自定义指令", "description": "所有模式通用规则\n当前语言:{{language}}", - "loadFromFile": "支持从.roo/rules/目录读取全局配置(.roorules和.clinerules已弃用并将很快停止工作)。" + "loadFromFile": "支持从.pearai-agent/rules/目录读取全局配置(.roorules和.clinerules已弃用并将很快停止工作)。" }, "systemPrompt": { "preview": "预览系统提示词", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "高级:覆盖系统提示词", - "description": "您可以通过在工作区创建文件 .roo/system-prompt-{{slug}},完全替换此模式的系统提示(角色定义和自定义指令除外)。这是一个非常高级的功能,会绕过内置的安全措施和一致性检查(尤其是与工具使用相关的部分),请谨慎操作!" + "description": "您可以通过在工作区创建文件 .pearai-agent/system-prompt-{{slug}},完全替换此模式的系统提示(角色定义和自定义指令除外)。这是一个非常高级的功能,会绕过内置的安全措施和一致性检查(尤其是与工具使用相关的部分),请谨慎操作!" }, "createModeDialog": { "title": "创建新模式", @@ -122,7 +122,7 @@ "description": "全局可用" }, "project": { - "label": "项目特定 (.roomodes)", + "label": "项目特定 (.pearai-agent-ignore)", "description": "仅当前项目有效" } }, diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 2d684f34366..e0482f7b335 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -298,9 +298,9 @@ "label": "工作区文件限制", "description": "允许纳入上下文的最大文件数(值越大消耗token越多)" }, - "rooignore": { - "label": "在列表和搜索中显示 .rooignore 文件", - "description": "启用后,与 .rooignore 中模式匹配的文件将在列表中显示锁定符号。禁用时,这些文件将从文件列表和搜索中完全隐藏。" + "pearai-agent-ignore": { + "label": "在列表和搜索中显示 .pearai-agent-ignore 文件", + "description": "启用后,与 .pearai-agent-ignore 中模式匹配的文件将在列表中显示锁定符号。禁用时,这些文件将从文件列表和搜索中完全隐藏。" }, "maxReadFile": { "label": "文件读取自动截断阈值", diff --git a/webview-ui/src/i18n/locales/zh-TW/prompts.json b/webview-ui/src/i18n/locales/zh-TW/prompts.json index 9b99707cf67..876175b83fd 100644 --- a/webview-ui/src/i18n/locales/zh-TW/prompts.json +++ b/webview-ui/src/i18n/locales/zh-TW/prompts.json @@ -6,7 +6,7 @@ "createNewMode": "建立新模式", "editModesConfig": "編輯模式設定", "editGlobalModes": "編輯全域模式", - "editProjectModes": "編輯專案模式 (.roomodes)", + "editProjectModes": "編輯專案模式 (.pearai-agent-ignore)", "createModeHelpText": "點選 + 建立新的自訂模式,或者在聊天中直接請 Roo 為您建立!" }, "apiConfiguration": { @@ -37,12 +37,12 @@ "title": "模式專屬自訂指令(選用)", "resetToDefault": "重設為預設值", "description": "為 {{modeName}} 模式新增專屬的行為指南。", - "loadFromFile": "{{mode}} 模式的自訂指令也可以從工作區的 .roo/rules-{{slug}}/ 資料夾載入(.roorules-{{slug}} 和 .clinerules-{{slug}} 已棄用並將很快停止運作)。" + "loadFromFile": "{{mode}} 模式的自訂指令也可以從工作區的 .pearai-agent/rules-{{slug}}/ 資料夾載入(.roorules-{{slug}} 和 .clinerules-{{slug}} 已棄用並將很快停止運作)。" }, "globalCustomInstructions": { "title": "所有模式的自訂指令", "description": "這些指令適用於所有模式。它們提供了一組基本行為,可以透過下方的模式專屬自訂指令來強化。\n如果您希望 Roo 使用與編輯器顯示語言 ({{language}}) 不同的語言來思考和對話,您可以在這裡指定。", - "loadFromFile": "指令也可以從工作區的 .roo/rules/ 資料夾載入(.roorules 和 .clinerules 已棄用並將很快停止運作)。" + "loadFromFile": "指令也可以從工作區的 .pearai-agent/rules/ 資料夾載入(.roorules 和 .clinerules 已棄用並將很快停止運作)。" }, "systemPrompt": { "preview": "預覽系統提示詞", @@ -101,7 +101,7 @@ }, "advancedSystemPrompt": { "title": "進階:覆寫系統提示詞", - "description": "您可以透過在工作區建立檔案 .roo/system-prompt-{{slug}} 來完全替換此模式的系統提示詞(角色定義和自訂指令除外)。這是一個非常進階的功能,會繞過內建的安全措施和一致性檢查(尤其是與工具使用相關的檢查),請謹慎使用!" + "description": "您可以透過在工作區建立檔案 .pearai-agent/system-prompt-{{slug}} 來完全替換此模式的系統提示詞(角色定義和自訂指令除外)。這是一個非常進階的功能,會繞過內建的安全措施和一致性檢查(尤其是與工具使用相關的檢查),請謹慎使用!" }, "createModeDialog": { "title": "建立新模式", @@ -122,7 +122,7 @@ "description": "在所有工作區可用" }, "project": { - "label": "專案特定 (.roomodes)", + "label": "專案特定 (.pearai-agent-ignore)", "description": "僅在此工作區可用,優先於全域模式" } }, diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 32b9f709769..cce91229045 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -298,9 +298,9 @@ "label": "工作區檔案的上下文限制", "description": "目前工作目錄中最多包含多少個檔案。數值越高提供的上下文越多,但 token 用量也會增加。" }, - "rooignore": { - "label": "在列表和搜尋中顯示被 .rooignore 排除的檔案", - "description": "啟用後,符合 .rooignore 規則的檔案會在列表中顯示並標示鎖定圖示。停用後,這些檔案將完全從檔案列表和搜尋結果中隱藏。" + "pearai-agent-ignore": { + "label": "在列表和搜尋中顯示被 .pearai-agent-ignore 排除的檔案", + "description": "啟用後,符合 .pearai-agent-ignore 規則的檔案會在列表中顯示並標示鎖定圖示。停用後,這些檔案將完全從檔案列表和搜尋結果中隱藏。" }, "maxReadFile": { "label": "檔案讀取自動截斷閾值", diff --git a/webview-ui/src/index.css b/webview-ui/src/index.css index f8bf1ab5a64..50603c6ac58 100644 --- a/webview-ui/src/index.css +++ b/webview-ui/src/index.css @@ -413,3 +413,93 @@ input[cmdk-input]:focus { .codicon[class*="codicon-"] { text-rendering: geometricPrecision !important; } + + +/* _-_-_-_-_-_-_-_- PEARAI CREATOR STYLES _-_-_-_-_-_-_-_- */ + +.circle { + position: absolute; + left: 50%; + top: 50%; + transform: translateX(-50%) translateY(-50%); + width: 10px; + height: 10px; +} + +/* Static green dot */ +.circle::after { + content: ""; + position: absolute; + left: 0; + top: 0; + display: block; + width: 100%; + height: 100%; + background-color: #75daad; + border-radius: 50px; + z-index: 2; +} + +/* Pulsing outer ring */ +.animated-circle::before { + content: ""; + position: absolute; + left: 0; + top: 0; + display: block; + width: 100%; + height: 100%; + border-radius: 50px; + background-color: #75daad; + opacity: 0.6; + z-index: 1; + animation: pulse-ring 1.5s cubic-bezier(0.215, 0.61, 0.355, 1) infinite; +} + +@keyframes pulse-ring { + 0% { + transform: scale(1); + opacity: 0.6; + } + 100% { + transform: scale(3); + opacity: 0; + } +} + +/* Rainbow border glow effect */ +.rainbow-border-glow { + position: absolute; + inset: -2px; + background: linear-gradient( + 90deg, + rgba(255, 128, 128, 0.7) 0%, + rgba(255, 192, 128, 0.7) 14%, + rgba(255, 255, 128, 0.7) 28%, + rgba(128, 255, 128, 0.7) 42%, + rgba(128, 255, 255, 0.7) 56%, + rgba(128, 128, 255, 0.7) 70%, + rgba(192, 128, 255, 0.7) 84%, + rgba(255, 128, 192, 0.7) 100% + ); + background-size: 200% auto; + filter: blur(5px); + animation: rainbow-flow 3s linear infinite; + z-index: -1; /* Ensure it's behind the button */ + opacity: 0; /* Start invisible */ + transition: opacity 0.5s ease; /* Add transition for smooth opacity changes */ +} + +/* Class to show the element */ +.rainbow-border-glow-visible { + opacity: 0.7; +} + +@keyframes rainbow-flow { + to { + background-position: 200% center; + } +} + + +/* _-_-_-_-_-_-_-_- PEARAI CREATOR STYLES _-_-_-_-_-_-_-_- */