├── .nvmrc ├── .rooignore ├── webview-ui ├── public │ └── .gitkeep ├── src │ ├── stories │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── Welcome.mdx │ │ ├── Progress.stories.tsx │ │ ├── AutosizeTextarea.stories.tsx │ │ ├── Slider.stories.tsx │ │ ├── Badge.stories.tsx │ │ ├── Collapsible.stories.tsx │ │ ├── Chat.stories.tsx │ │ └── Button.stories.ts │ ├── i18n │ │ ├── locales │ │ │ ├── ca │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── de │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── en │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── es │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── fr │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── hi │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── it │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── ja │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ ├── history.json │ │ │ │ └── mcp.json │ │ │ ├── ko │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ ├── history.json │ │ │ │ └── mcp.json │ │ │ ├── pl │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── pt-BR │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── tr │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ │ ├── zh-CN │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ ├── history.json │ │ │ │ └── mcp.json │ │ │ ├── zh-TW │ │ │ │ ├── .gitkeep │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ ├── history.json │ │ │ │ └── mcp.json │ │ │ └── vi │ │ │ │ ├── common.json │ │ │ │ ├── humanRelay.json │ │ │ │ ├── welcome.json │ │ │ │ └── history.json │ │ ├── test-utils.ts │ │ ├── __tests__ │ │ │ └── TranslationContext.test.tsx │ │ ├── TranslationContext.tsx │ │ └── setup.ts │ ├── vite-env.d.ts │ ├── components │ │ ├── ui │ │ │ ├── markdown │ │ │ │ ├── index.ts │ │ │ │ └── Blockquote.tsx │ │ │ ├── chat │ │ │ │ ├── index.ts │ │ │ │ ├── ChatProvider.ts │ │ │ │ ├── useChatUI.ts │ │ │ │ ├── useChatInput.ts │ │ │ │ ├── ChatMessageProvider.ts │ │ │ │ ├── useChatMessage.ts │ │ │ │ ├── ChatInputProvider.ts │ │ │ │ ├── Chat.tsx │ │ │ │ ├── types.ts │ │ │ │ └── ChatMessages.tsx │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useRooPortal.ts │ │ │ │ ├── useClipboard.ts │ │ │ │ ├── useRequestyKeyInfo.ts │ │ │ │ └── useOpenRouterKeyInfo.ts │ │ │ ├── collapsible.tsx │ │ │ ├── index.ts │ │ │ ├── scroll-area.tsx │ │ │ ├── separator.tsx │ │ │ ├── input.tsx │ │ │ ├── progress.tsx │ │ │ ├── textarea.tsx │ │ │ ├── slider.tsx │ │ │ ├── badge.tsx │ │ │ ├── tooltip.tsx │ │ │ ├── popover.tsx │ │ │ ├── confirmation-dialog.tsx │ │ │ └── card.tsx │ │ ├── common │ │ │ ├── __mocks__ │ │ │ │ ├── CodeBlock.tsx │ │ │ │ └── MarkdownBlock.tsx │ │ │ └── Tab.tsx │ │ ├── scheduler │ │ │ ├── LabeledInput.tsx │ │ │ ├── types.ts │ │ │ ├── DaySelector.tsx │ │ │ ├── ScheduleList.tsx │ │ │ └── DateTimeSelector.tsx │ │ └── welcome │ │ │ └── WelcomeView.tsx │ ├── types.d.ts │ ├── lib │ │ └── utils.ts │ ├── utils │ │ ├── formatPrice.ts │ │ ├── TelemetryClient.ts │ │ ├── format.ts │ │ ├── highlight.ts │ │ ├── useDebounceEffect.ts │ │ ├── path-mentions.ts │ │ ├── clipboard.ts │ │ └── getLanguageFromPath.ts │ ├── __mocks__ │ │ ├── posthog-js.ts │ │ ├── lucide-react.ts │ │ ├── components │ │ │ └── chat │ │ │ │ └── TaskHeader.tsx │ │ ├── pretty-bytes.js │ │ ├── vscrui.ts │ │ └── i18n │ │ │ ├── setup.ts │ │ │ └── TranslationContext.tsx │ ├── index.tsx │ ├── oauth │ │ └── urls.ts │ ├── setupTests.ts │ └── App.tsx ├── .npmrc ├── .vite │ └── deps │ │ ├── package.json │ │ └── _metadata.json ├── .eslintrc.json ├── index.html ├── .gitignore ├── components.json ├── tsconfig.json ├── jest.config.cjs └── vite.config.ts ├── .tool-versions ├── .npmrc ├── .gitconfig ├── .prettierignore ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── dependabot.yml └── pull_request_template.md ├── assets └── icons │ ├── scheduler-icon.png │ ├── roo-icon-black.svg │ ├── roo-icon-white.svg │ └── scheduler-icon.svg ├── .gitattributes ├── .prettierrc.json ├── src ├── __mocks__ │ ├── os-name.js │ ├── delay.js │ ├── strip-ansi.js │ ├── globby.js │ ├── get-folder-size.js │ ├── @modelcontextprotocol │ │ └── sdk │ │ │ ├── client │ │ │ ├── index.js │ │ │ ├── sse.js │ │ │ └── stdio.js │ │ │ ├── index.js │ │ │ └── types.js │ ├── default-shell.js │ ├── strip-bom.js │ ├── McpHub.ts │ ├── serialize-error.js │ ├── p-wait-for.js │ └── jest.setup.ts ├── utils │ ├── type-fu.ts │ ├── logging │ │ ├── index.ts │ │ └── __tests__ │ │ │ └── MockTransport.ts │ ├── xml.ts │ ├── pathUtils.ts │ ├── fs.ts │ └── tts.ts ├── i18n │ ├── locales │ │ ├── ko │ │ │ └── tools.json │ │ ├── ja │ │ │ └── tools.json │ │ ├── zh-CN │ │ │ └── tools.json │ │ ├── zh-TW │ │ │ └── tools.json │ │ ├── vi │ │ │ └── tools.json │ │ ├── en │ │ │ └── tools.json │ │ ├── it │ │ │ └── tools.json │ │ ├── tr │ │ │ └── tools.json │ │ ├── pl │ │ │ └── tools.json │ │ ├── ca │ │ │ └── tools.json │ │ ├── de │ │ │ └── tools.json │ │ ├── es │ │ │ └── tools.json │ │ ├── fr │ │ │ └── tools.json │ │ ├── pt-BR │ │ │ └── tools.json │ │ └── hi │ │ │ └── tools.json │ └── index.ts ├── shared │ ├── vsCodeSelectorUtils.ts │ ├── globalFileNames.ts │ ├── __tests__ │ │ ├── language.test.ts │ │ └── vsCodeSelectorUtils.test.ts │ └── language.ts ├── schemas │ └── __tests__ │ │ └── index.test.ts ├── core │ └── webview │ │ ├── getNonce.ts │ │ └── getUri.ts ├── integrations │ └── misc │ │ ├── line-counter.ts │ │ └── process-images.ts └── __tests__ │ └── extension.test.ts ├── .git-blame-ignore-revs ├── .vscode ├── extensions.json ├── settings.json ├── launch.json └── tasks.json ├── e2e ├── tsconfig.json ├── .vscode-test.mjs ├── rooservice-test.mjs ├── package.json └── src │ ├── suite │ └── extension.test.ts │ └── runTest.ts ├── .gitignore ├── .eslintrc.json ├── flake.lock ├── flake.nix ├── PRIVACY.md ├── knip.json ├── .roo ├── rules-translate │ ├── instructions-zh-tw.md │ └── instructions-de.md └── rules │ └── rules.md ├── tsconfig.json ├── CHANGELOG.md ├── ellipsis.yaml ├── .vscodeignore ├── CONTRIBUTING.md └── scheduler-jest.config.js /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.18.1 2 | -------------------------------------------------------------------------------- /.rooignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /webview-ui/public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs v20.18.1 2 | -------------------------------------------------------------------------------- /webview-ui/src/stories/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/ca/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/de/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/en/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/es/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/fr/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/hi/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/it/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/ja/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/ko/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/pl/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/pt-BR/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/tr/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/zh-CN/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/zh-TW/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webview-ui/.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ -------------------------------------------------------------------------------- /.gitconfig: -------------------------------------------------------------------------------- 1 | [blame] 2 | ignoreRevsFile = .git-blame-ignore-revs 3 | -------------------------------------------------------------------------------- /webview-ui/.vite/deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /webview-ui/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/markdown/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Markdown" 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules 3 | webview-ui/build/ 4 | CHANGELOG.md 5 | package-lock.json -------------------------------------------------------------------------------- /webview-ui/src/components/ui/chat/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types" 2 | export * from "./Chat" 3 | -------------------------------------------------------------------------------- /webview-ui/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "react-app", 3 | "ignorePatterns": ["!.storybook"] 4 | } 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in the repo 2 | * @mrubens @cte 3 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./useClipboard" 2 | export * from "./useRooPortal" 3 | -------------------------------------------------------------------------------- /assets/icons/scheduler-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyle-apex/roo-scheduler/HEAD/assets/icons/scheduler-icon.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | demo.gif filter=lfs diff=lfs merge=lfs -text 2 | assets/docs/demo.gif filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": true, 4 | "printWidth": 120, 5 | "semi": false, 6 | "bracketSameLine": true 7 | } 8 | -------------------------------------------------------------------------------- /src/__mocks__/os-name.js: -------------------------------------------------------------------------------- 1 | function osName() { 2 | return "macOS" 3 | } 4 | 5 | module.exports = osName 6 | module.exports.default = osName 7 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/ca/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/de/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/en/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/es/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/fr/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/hi/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/it/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/ja/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/ko/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/pl/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/tr/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/vi/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/pt-BR/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/zh-CN/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/zh-TW/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "number_format": { 3 | "thousand_suffix": "k", 4 | "million_suffix": "m", 5 | "billion_suffix": "b" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/__mocks__/delay.js: -------------------------------------------------------------------------------- 1 | function delay(ms) { 2 | return new Promise((resolve) => setTimeout(resolve, ms)) 3 | } 4 | 5 | module.exports = delay 6 | module.exports.default = delay 7 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Ran Prettier on all files - https://github.com/RooVetGit/Roo-Code/pull/404 2 | 60a0a824b96a0b326af4d8871b6903f4ddcfe114 3 | 579bdd9dbf6d2d569e5e7adb5ff6292b1e42ea34 4 | -------------------------------------------------------------------------------- /webview-ui/src/types.d.ts: -------------------------------------------------------------------------------- 1 | // Type declarations for third-party modules 2 | 3 | declare module "knuth-shuffle-seeded" { 4 | export default function knuthShuffle(array: T[], seed: any): T[] 5 | } 6 | -------------------------------------------------------------------------------- /webview-ui/.vite/deps/_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "1f16633b", 3 | "configHash": "33de4323", 4 | "lockfileHash": "237188ca", 5 | "browserHash": "7afc7d0e", 6 | "optimized": {}, 7 | "chunks": {} 8 | } -------------------------------------------------------------------------------- /src/__mocks__/strip-ansi.js: -------------------------------------------------------------------------------- 1 | function stripAnsi(string) { 2 | // Simple mock that just returns the input string 3 | return string 4 | } 5 | 6 | module.exports = stripAnsi 7 | module.exports.default = stripAnsi 8 | -------------------------------------------------------------------------------- /webview-ui/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/markdown/Blockquote.tsx: -------------------------------------------------------------------------------- 1 | export const Blockquote = ({ children }: { children: React.ReactNode }) => { 2 | return
{children}
3 | } 4 | -------------------------------------------------------------------------------- /webview-ui/src/stories/Welcome.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from "@storybook/blocks"; 2 | 3 | 4 | 5 | # Welcome 6 | 7 | This Roo Code storybook is used to independently develop components for the Roo Code webview UI. 8 | -------------------------------------------------------------------------------- /src/__mocks__/globby.js: -------------------------------------------------------------------------------- 1 | function globby(patterns, options) { 2 | return Promise.resolve([]) 3 | } 4 | 5 | globby.sync = function (patterns, options) { 6 | return [] 7 | } 8 | 9 | module.exports = globby 10 | module.exports.default = globby 11 | -------------------------------------------------------------------------------- /src/utils/type-fu.ts: -------------------------------------------------------------------------------- 1 | export type Keys = keyof T 2 | 3 | export type Values = T[keyof T] 4 | 5 | export type Equals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false 6 | 7 | export type AssertEqual = T 8 | -------------------------------------------------------------------------------- /webview-ui/src/utils/formatPrice.ts: -------------------------------------------------------------------------------- 1 | export const formatPrice = (price: number) => { 2 | return new Intl.NumberFormat("en-US", { 3 | style: "currency", 4 | currency: "USD", 5 | minimumFractionDigits: 2, 6 | maximumFractionDigits: 2, 7 | }).format(price) 8 | } 9 | -------------------------------------------------------------------------------- /src/i18n/locales/ko/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " ({{start}}-{{end}}행)", 4 | "linesFromToEnd": " ({{start}}행-끝)", 5 | "linesFromStartTo": " (1-{{end}}행)", 6 | "definitionsOnly": " (정의만)", 7 | "maxLines": " (최대 {{max}}행)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/ja/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " ({{start}}-{{end}}行目)", 4 | "linesFromToEnd": " ({{start}}行目-最後まで)", 5 | "linesFromStartTo": " (1-{{end}}行目)", 6 | "definitionsOnly": " (定義のみ)", 7 | "maxLines": " (最大{{max}}行)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/zh-CN/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (第 {{start}}-{{end}} 行)", 4 | "linesFromToEnd": " (第 {{start}} 行至末尾)", 5 | "linesFromStartTo": " (第 1-{{end}} 行)", 6 | "definitionsOnly": " (仅定义)", 7 | "maxLines": " (最多 {{max}} 行)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/zh-TW/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (第 {{start}}-{{end}} 行)", 4 | "linesFromToEnd": " (第 {{start}} 行至結尾)", 5 | "linesFromStartTo": " (第 1-{{end}} 行)", 6 | "definitionsOnly": " (僅定義)", 7 | "maxLines": " (最多 {{max}} 行)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/__mocks__/get-folder-size.js: -------------------------------------------------------------------------------- 1 | module.exports = async function getFolderSize() { 2 | return { 3 | size: 1000, 4 | errors: [], 5 | } 6 | } 7 | 8 | module.exports.loose = async function getFolderSizeLoose() { 9 | return { 10 | size: 1000, 11 | errors: [], 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/i18n/locales/vi/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (dòng {{start}}-{{end}})", 4 | "linesFromToEnd": " (dòng {{start}}-cuối)", 5 | "linesFromStartTo": " (dòng 1-{{end}})", 6 | "definitionsOnly": " (chỉ định nghĩa)", 7 | "maxLines": " (tối đa {{max}} dòng)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug 3 | labels: ["bug"] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: What happened? 9 | description: Describe the bug. 10 | validations: 11 | required: true 12 | -------------------------------------------------------------------------------- /src/i18n/locales/en/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (lines {{start}}-{{end}})", 4 | "linesFromToEnd": " (lines {{start}}-end)", 5 | "linesFromStartTo": " (lines 1-{{end}})", 6 | "definitionsOnly": " (definitions only)", 7 | "maxLines": " (max {{max}} lines)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/it/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (righe {{start}}-{{end}})", 4 | "linesFromToEnd": " (righe {{start}}-fine)", 5 | "linesFromStartTo": " (righe 1-{{end}})", 6 | "definitionsOnly": " (solo definizioni)", 7 | "maxLines": " (max {{max}} righe)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/tr/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (satır {{start}}-{{end}})", 4 | "linesFromToEnd": " (satır {{start}}-son)", 5 | "linesFromStartTo": " (satır 1-{{end}})", 6 | "definitionsOnly": " (sadece tanımlar)", 7 | "maxLines": " (maks. {{max}} satır)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/__mocks__/@modelcontextprotocol/sdk/client/index.js: -------------------------------------------------------------------------------- 1 | class Client { 2 | constructor() { 3 | this.request = jest.fn() 4 | } 5 | 6 | connect() { 7 | return Promise.resolve() 8 | } 9 | 10 | close() { 11 | return Promise.resolve() 12 | } 13 | } 14 | 15 | module.exports = { 16 | Client, 17 | } 18 | -------------------------------------------------------------------------------- /src/i18n/locales/pl/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (linie {{start}}-{{end}})", 4 | "linesFromToEnd": " (linie {{start}}-koniec)", 5 | "linesFromStartTo": " (linie 1-{{end}})", 6 | "definitionsOnly": " (tylko definicje)", 7 | "maxLines": " (maks. {{max}} linii)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/ca/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (línies {{start}}-{{end}})", 4 | "linesFromToEnd": " (línies {{start}}-final)", 5 | "linesFromStartTo": " (línies 1-{{end}})", 6 | "definitionsOnly": " (només definicions)", 7 | "maxLines": " (màxim {{max}} línies)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/de/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (Zeilen {{start}}-{{end}})", 4 | "linesFromToEnd": " (Zeilen {{start}}-Ende)", 5 | "linesFromStartTo": " (Zeilen 1-{{end}})", 6 | "definitionsOnly": " (nur Definitionen)", 7 | "maxLines": " (maximal {{max}} Zeilen)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/es/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (líneas {{start}}-{{end}})", 4 | "linesFromToEnd": " (líneas {{start}}-final)", 5 | "linesFromStartTo": " (líneas 1-{{end}})", 6 | "definitionsOnly": " (solo definiciones)", 7 | "maxLines": " (máximo {{max}} líneas)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/fr/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (lignes {{start}}-{{end}})", 4 | "linesFromToEnd": " (lignes {{start}}-fin)", 5 | "linesFromStartTo": " (lignes 1-{{end}})", 6 | "definitionsOnly": " (définitions uniquement)", 7 | "maxLines": " (max {{max}} lignes)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/locales/pt-BR/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (linhas {{start}}-{{end}})", 4 | "linesFromToEnd": " (linhas {{start}}-fim)", 5 | "linesFromStartTo": " (linhas 1-{{end}})", 6 | "definitionsOnly": " (apenas definições)", 7 | "maxLines": " (máx. {{max}} linhas)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /webview-ui/src/__mocks__/posthog-js.ts: -------------------------------------------------------------------------------- 1 | // Mock implementation of posthog-js 2 | const posthogMock = { 3 | init: jest.fn(), 4 | capture: jest.fn(), 5 | opt_in_capturing: jest.fn(), 6 | opt_out_capturing: jest.fn(), 7 | reset: jest.fn(), 8 | identify: jest.fn(), 9 | } 10 | 11 | export default posthogMock 12 | -------------------------------------------------------------------------------- /src/i18n/locales/hi/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "readFile": { 3 | "linesRange": " (पंक्तियाँ {{start}}-{{end}})", 4 | "linesFromToEnd": " (पंक्तियाँ {{start}}-अंत)", 5 | "linesFromStartTo": " (पंक्तियाँ 1-{{end}})", 6 | "definitionsOnly": " (केवल परिभाषाएँ)", 7 | "maxLines": " (अधिकतम {{max}} पंक्तियाँ)" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/__mocks__/default-shell.js: -------------------------------------------------------------------------------- 1 | // Mock default shell based on platform 2 | const os = require("os") 3 | 4 | let defaultShell 5 | if (os.platform() === "win32") { 6 | defaultShell = "cmd.exe" 7 | } else { 8 | defaultShell = "/bin/bash" 9 | } 10 | 11 | module.exports = defaultShell 12 | module.exports.default = defaultShell 13 | -------------------------------------------------------------------------------- /webview-ui/src/components/common/__mocks__/CodeBlock.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | interface CodeBlockProps { 4 | children?: React.ReactNode 5 | language?: string 6 | } 7 | 8 | const CodeBlock: React.FC = () =>
Mocked Code Block
9 | 10 | export default CodeBlock 11 | -------------------------------------------------------------------------------- /src/__mocks__/strip-bom.js: -------------------------------------------------------------------------------- 1 | // Mock implementation of strip-bom 2 | module.exports = function stripBom(string) { 3 | if (typeof string !== "string") { 4 | throw new TypeError("Expected a string") 5 | } 6 | 7 | // Removes UTF-8 BOM 8 | if (string.charCodeAt(0) === 0xfeff) { 9 | return string.slice(1) 10 | } 11 | 12 | return string 13 | } 14 | -------------------------------------------------------------------------------- /src/shared/vsCodeSelectorUtils.ts: -------------------------------------------------------------------------------- 1 | import { LanguageModelChatSelector } from "vscode" 2 | 3 | export const SELECTOR_SEPARATOR = "/" 4 | 5 | export function stringifyVsCodeLmModelSelector(selector: LanguageModelChatSelector): string { 6 | return [selector.vendor, selector.family, selector.version, selector.id].filter(Boolean).join(SELECTOR_SEPARATOR) 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/chat/ChatProvider.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from "react" 2 | 3 | import { ChatHandler } from "./types" 4 | 5 | type ChatContext = ChatHandler & { 6 | assistantName: string 7 | } 8 | 9 | export const chatContext = createContext(null) 10 | 11 | export const ChatProvider = chatContext.Provider 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode", 7 | "csstools.postcss", 8 | "bradlc.vscode-tailwindcss", 9 | "connor4312.esbuild-problem-matchers" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /webview-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Roo Code 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/hooks/useRooPortal.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { useMount } from "react-use" 3 | 4 | export const useRooPortal = (id: string) => { 5 | const [container, setContainer] = useState() 6 | 7 | useMount(() => setContainer(document.getElementById(id) ?? undefined)) 8 | 9 | return container 10 | } 11 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/zh-CN/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "人工辅助模式", 3 | "dialogDescription": "操作指南:\n1. 复制下方提示词到外部AI工具\n2. 将得到的结果粘贴至输入框", 4 | "copiedToClipboard": "已复制到剪贴板", 5 | "aiResponse": { 6 | "label": "AI响应内容:", 7 | "placeholder": "请在此处粘贴结果..." 8 | }, 9 | "actions": { 10 | "cancel": "取消", 11 | "submit": "提交" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react" 2 | import { createRoot } from "react-dom/client" 3 | 4 | import "./index.css" 5 | import App from "./App" 6 | import "../../node_modules/@vscode/codicons/dist/codicon.css" 7 | 8 | createRoot(document.getElementById("root")!).render( 9 | 10 | 11 | , 12 | ) 13 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/chat/useChatUI.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react" 2 | 3 | import { chatContext } from "./ChatProvider" 4 | 5 | export const useChatUI = () => { 6 | const context = useContext(chatContext) 7 | 8 | if (!context) { 9 | throw new Error("useChatUI must be used within a ChatProvider") 10 | } 11 | 12 | return context 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/zh-TW/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "人工中繼 - 請協助複製貼上訊息", 3 | "dialogDescription": "請將以下提示詞複製到網頁 AI,然後將 AI 的回覆貼上到下方的輸入框中。", 4 | "copiedToClipboard": "已複製到剪貼簿", 5 | "aiResponse": { 6 | "label": "請輸入 AI 的回覆:", 7 | "placeholder": "在此貼上 AI 的回覆..." 8 | }, 9 | "actions": { 10 | "cancel": "取消", 11 | "submit": "提交" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest a new feature 3 | labels: ["enhancement"] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: What feature do you want? 9 | description: Describe the feature or improvement you would like to see. 10 | validations: 11 | required: true -------------------------------------------------------------------------------- /webview-ui/src/components/common/__mocks__/MarkdownBlock.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | interface MarkdownBlockProps { 4 | children?: React.ReactNode 5 | content?: string 6 | } 7 | 8 | const MarkdownBlock: React.FC = ({ content }) => ( 9 |
{content}
10 | ) 11 | 12 | export default MarkdownBlock 13 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/chat/useChatInput.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react" 2 | 3 | import { chatInputContext } from "./ChatInputProvider" 4 | 5 | export const useChatInput = () => { 6 | const context = useContext(chatInputContext) 7 | 8 | if (!context) { 9 | throw new Error("useChatInput must be used within a ChatInputProvider") 10 | } 11 | 12 | return context 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 2 | 3 | const Collapsible = CollapsiblePrimitive.Root 4 | 5 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 6 | 7 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 8 | 9 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 10 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/chat/ChatMessageProvider.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from "react" 2 | 3 | import { Message } from "./types" 4 | 5 | export interface ChatMessageContext { 6 | message: Message 7 | isLast: boolean 8 | } 9 | 10 | export const chatMessageContext = createContext(null) 11 | 12 | export const ChatMessageProvider = chatMessageContext.Provider 13 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/ko/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "휴먼 릴레이 - 정보 복사/붙여넣기를 도와주세요", 3 | "dialogDescription": "다음 프롬프트를 웹 AI에 복사하고, AI의 응답을 아래 입력창에 붙여넣어 주세요.", 4 | "copiedToClipboard": "클립보드에 복사됨", 5 | "aiResponse": { 6 | "label": "AI의 응답을 입력해주세요:", 7 | "placeholder": "여기에 AI의 응답을 붙여넣으세요..." 8 | }, 9 | "actions": { 10 | "cancel": "취소", 11 | "submit": "제출" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/chat/useChatMessage.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react" 2 | 3 | import { chatMessageContext } from "./ChatMessageProvider" 4 | 5 | export const useChatMessage = () => { 6 | const context = useContext(chatMessageContext) 7 | 8 | if (!context) { 9 | throw new Error("useChatMessage must be used within a ChatMessageProvider") 10 | } 11 | 12 | return context 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/ja/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "ヒューマンリレー - 情報のコピー&ペーストにご協力ください", 3 | "dialogDescription": "以下のプロンプトをウェブAIにコピーし、AIの応答を下の入力ボックスにペーストしてください。", 4 | "copiedToClipboard": "クリップボードにコピーしました", 5 | "aiResponse": { 6 | "label": "AIの応答を入力してください:", 7 | "placeholder": "ここにAIの応答をペースト..." 8 | }, 9 | "actions": { 10 | "cancel": "キャンセル", 11 | "submit": "送信" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/__mocks__/@modelcontextprotocol/sdk/client/sse.js: -------------------------------------------------------------------------------- 1 | class SSEClientTransport { 2 | constructor(url, options = {}) { 3 | this.url = url 4 | this.options = options 5 | this.onerror = null 6 | this.connect = jest.fn().mockResolvedValue() 7 | this.close = jest.fn().mockResolvedValue() 8 | this.start = jest.fn().mockResolvedValue() 9 | } 10 | } 11 | 12 | module.exports = { 13 | SSEClientTransport, 14 | } 15 | -------------------------------------------------------------------------------- /src/shared/globalFileNames.ts: -------------------------------------------------------------------------------- 1 | export const GlobalFileNames = { 2 | apiConversationHistory: "api_conversation_history.json", 3 | uiMessages: "ui_messages.json", 4 | glamaModels: "glama_models.json", 5 | openRouterModels: "openrouter_models.json", 6 | requestyModels: "requesty_models.json", 7 | mcpSettings: "mcp_settings.json", 8 | unboundModels: "unbound_models.json", 9 | customModes: "custom_modes.yaml", 10 | } 11 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/chat/ChatInputProvider.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from "react" 2 | 3 | interface ChatInputContext { 4 | isDisabled: boolean 5 | handleKeyDown: (e: React.KeyboardEvent) => void 6 | handleSubmit: (e: React.FormEvent) => void 7 | } 8 | 9 | export const chatInputContext = createContext(null) 10 | 11 | export const ChatInputProvider = chatInputContext.Provider 12 | -------------------------------------------------------------------------------- /webview-ui/src/__mocks__/lucide-react.ts: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export const Check = () => React.createElement("div") 4 | export const ChevronsUpDown = () => React.createElement("div") 5 | export const Loader = () => React.createElement("div") 6 | export const X = () => React.createElement("div") 7 | export const Edit = () => React.createElement("div") 8 | export const Database = (props: any) => React.createElement("span", { "data-testid": "database-icon", ...props }) 9 | -------------------------------------------------------------------------------- /webview-ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | *storybook.log 26 | 27 | tsconfig.tsbuildinfo 28 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "moduleResolution": "Node", 5 | "esModuleInterop": true, 6 | "target": "ES2022", 7 | "lib": ["ES2022", "ESNext.Disposable", "DOM"], 8 | "sourceMap": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "useUnknownInCatchVariables": false, 12 | "outDir": "out" 13 | }, 14 | "include": ["src", "../src/exports/roo-code.d.ts"], 15 | "exclude": [".vscode-test", "**/node_modules/**", "out"] 16 | } 17 | -------------------------------------------------------------------------------- /webview-ui/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/index.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/en/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "Human Relay - Please Help Copy/Paste Information", 3 | "dialogDescription": "Please copy the following prompt to the web AI, then paste the AI's response in the input box below.", 4 | "copiedToClipboard": "Copied to clipboard", 5 | "aiResponse": { 6 | "label": "Please enter AI's response:", 7 | "placeholder": "Paste AI's response here..." 8 | }, 9 | "actions": { 10 | "cancel": "Cancel", 11 | "submit": "Submit" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/pl/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "Przekaźnik Ludzki - Pomóż skopiować/wkleić informacje", 3 | "dialogDescription": "Skopiuj poniższy prompt do AI w przeglądarce, a następnie wklej odpowiedź AI do pola wprowadzania poniżej.", 4 | "copiedToClipboard": "Skopiowano do schowka", 5 | "aiResponse": { 6 | "label": "Wprowadź odpowiedź AI:", 7 | "placeholder": "Wklej tutaj odpowiedź AI..." 8 | }, 9 | "actions": { 10 | "cancel": "Anuluj", 11 | "submit": "Wyślij" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/__mocks__/@modelcontextprotocol/sdk/client/stdio.js: -------------------------------------------------------------------------------- 1 | class StdioClientTransport { 2 | constructor() { 3 | this.start = jest.fn().mockResolvedValue(undefined) 4 | this.close = jest.fn().mockResolvedValue(undefined) 5 | this.stderr = { 6 | on: jest.fn(), 7 | } 8 | } 9 | } 10 | 11 | class StdioServerParameters { 12 | constructor() { 13 | this.command = "" 14 | this.args = [] 15 | this.env = {} 16 | } 17 | } 18 | 19 | module.exports = { 20 | StdioClientTransport, 21 | StdioServerParameters, 22 | } 23 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/vi/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "Chuyển tiếp Thủ công - Vui lòng giúp Sao chép/Dán thông tin", 3 | "dialogDescription": "Vui lòng sao chép đoạn nhắc sau vào AI web, sau đó dán phản hồi của AI vào hộp nhập liệu bên dưới.", 4 | "copiedToClipboard": "Đã sao chép vào clipboard", 5 | "aiResponse": { 6 | "label": "Vui lòng nhập phản hồi của AI:", 7 | "placeholder": "Dán phản hồi của AI vào đây..." 8 | }, 9 | "actions": { 10 | "cancel": "Hủy", 11 | "submit": "Gửi" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/it/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "Relay Umano - Aiuta a copiare/incollare le informazioni", 3 | "dialogDescription": "Copia il seguente prompt nell'AI web, quindi incolla la risposta dell'AI nella casella di input sottostante.", 4 | "copiedToClipboard": "Copiato negli appunti", 5 | "aiResponse": { 6 | "label": "Inserisci la risposta dell'AI:", 7 | "placeholder": "Incolla qui la risposta dell'AI..." 8 | }, 9 | "actions": { 10 | "cancel": "Annulla", 11 | "submit": "Invia" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/tr/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "İnsan Aktarımı - Lütfen bilgileri kopyalama/yapıştırmada yardımcı olun", 3 | "dialogDescription": "Lütfen aşağıdaki metni web AI'ya kopyalayın, ardından AI'nın yanıtını aşağıdaki giriş kutusuna yapıştırın.", 4 | "copiedToClipboard": "Panoya kopyalandı", 5 | "aiResponse": { 6 | "label": "Lütfen AI yanıtını girin:", 7 | "placeholder": "AI yanıtını buraya yapıştırın..." 8 | }, 9 | "actions": { 10 | "cancel": "İptal", 11 | "submit": "Gönder" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/__mocks__/McpHub.ts: -------------------------------------------------------------------------------- 1 | export class McpHub { 2 | connections = [] 3 | isConnecting = false 4 | 5 | constructor() { 6 | this.toggleToolAlwaysAllow = jest.fn() 7 | this.callTool = jest.fn() 8 | } 9 | 10 | async toggleToolAlwaysAllow(serverName: string, toolName: string, shouldAllow: boolean): Promise { 11 | return Promise.resolve() 12 | } 13 | 14 | async callTool(serverName: string, toolName: string, toolArguments?: Record): Promise { 15 | return Promise.resolve({ result: "success" }) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/pt-BR/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "Retransmissão Humana - Ajude a copiar/colar informações", 3 | "dialogDescription": "Por favor, copie o seguinte prompt para a IA web e depois cole a resposta da IA na caixa de entrada abaixo.", 4 | "copiedToClipboard": "Copiado para a área de transferência", 5 | "aiResponse": { 6 | "label": "Digite a resposta da IA:", 7 | "placeholder": "Cole a resposta da IA aqui..." 8 | }, 9 | "actions": { 10 | "cancel": "Cancelar", 11 | "submit": "Enviar" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/__mocks__/components/chat/TaskHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | // Import the actual utility instead of reimplementing it 3 | import { getMaxTokensForModel } from "@/utils/model-utils" 4 | 5 | // Re-export the utility function to maintain the same interface 6 | export { getMaxTokensForModel } 7 | 8 | /** 9 | * Mock version of the TaskHeader component 10 | */ 11 | const TaskHeader: React.FC = () => { 12 | return
Mocked TaskHeader
13 | } 14 | 15 | export default TaskHeader 16 | -------------------------------------------------------------------------------- /webview-ui/src/__mocks__/pretty-bytes.js: -------------------------------------------------------------------------------- 1 | module.exports = function prettyBytes(bytes) { 2 | if (typeof bytes !== "number") { 3 | throw new TypeError("Expected a number") 4 | } 5 | 6 | // Simple mock implementation that returns formatted strings. 7 | if (bytes === 0) return "0 B" 8 | if (bytes < 1024) return `${bytes} B` 9 | if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` 10 | if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB` 11 | return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB` 12 | } 13 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/fr/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "Relais Humain - Veuillez aider à copier/coller les informations", 3 | "dialogDescription": "Veuillez copier le texte suivant dans l'IA web, puis collez la réponse de l'IA dans la zone de saisie ci-dessous.", 4 | "copiedToClipboard": "Copié dans le presse-papiers", 5 | "aiResponse": { 6 | "label": "Veuillez saisir la réponse de l'IA :", 7 | "placeholder": "Collez la réponse de l'IA ici..." 8 | }, 9 | "actions": { 10 | "cancel": "Annuler", 11 | "submit": "Envoyer" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/hi/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "मानव रिले - कृपया जानकारी कॉपी-पेस्ट करने में सहायता करें", 3 | "dialogDescription": "कृपया निम्नलिखित प्रॉम्प्ट को वेब AI में कॉपी करें, फिर AI की प्रतिक्रिया को नीचे दिए गए इनपुट बॉक्स में पेस्ट करें।", 4 | "copiedToClipboard": "क्लिपबोर्ड पर कॉपी किया गया", 5 | "aiResponse": { 6 | "label": "कृपया AI की प्रतिक्रिया दर्ज करें:", 7 | "placeholder": "यहाँ AI की प्रतिक्रिया पेस्ट करें..." 8 | }, 9 | "actions": { 10 | "cancel": "रद्द करें", 11 | "submit": "जमा करें" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pnpm-store 2 | dist 3 | out 4 | out-* 5 | node_modules 6 | coverage/ 7 | .roomodes 8 | system-prompt-* 9 | 10 | .DS_Store 11 | 12 | # Builds 13 | bin/ 14 | roo-cline-*.vsix 15 | 16 | # Local prompts and rules 17 | /local-prompts 18 | 19 | # Test environment 20 | .test_env 21 | .vscode-test/ 22 | 23 | # Docs 24 | docs/_site/ 25 | 26 | # Dotenv 27 | .env 28 | .env.* 29 | !.env.*.sample 30 | 31 | 32 | #Local lint config 33 | .eslintrc.local.json 34 | 35 | #Logging 36 | logs 37 | 38 | # Vite development 39 | .vite-port 40 | .roomodes 41 | squash.sh 42 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/ca/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "Relé Humà - Si us plau, ajudeu a copiar/enganxar informació", 3 | "dialogDescription": "Si us plau, copieu el següent prompt a la IA web, després enganxeu la resposta de la IA al quadre d'entrada de sota.", 4 | "copiedToClipboard": "Copiat al porta-retalls", 5 | "aiResponse": { 6 | "label": "Si us plau, introduïu la resposta de la IA:", 7 | "placeholder": "Enganxeu aquí la resposta de la IA..." 8 | }, 9 | "actions": { 10 | "cancel": "Cancel·lar", 11 | "submit": "Enviar" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/de/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "Menschliche Weiterleitung - Bitte hilf beim Kopieren/Einfügen von Informationen", 3 | "dialogDescription": "Bitte kopiere den folgenden Prompt in die Web-KI und füge dann die Antwort der KI in das Eingabefeld unten ein.", 4 | "copiedToClipboard": "In die Zwischenablage kopiert", 5 | "aiResponse": { 6 | "label": "Bitte gib die KI-Antwort ein:", 7 | "placeholder": "KI-Antwort hier einfügen..." 8 | }, 9 | "actions": { 10 | "cancel": "Abbrechen", 11 | "submit": "Absenden" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/es/humanRelay.json: -------------------------------------------------------------------------------- 1 | { 2 | "dialogTitle": "Relevo Humano - Por favor ayude a copiar/pegar información", 3 | "dialogDescription": "Por favor copie el siguiente mensaje en la IA web, luego pegue la respuesta de la IA en el cuadro de entrada a continuación.", 4 | "copiedToClipboard": "Copiado al portapapeles", 5 | "aiResponse": { 6 | "label": "Por favor ingrese la respuesta de la IA:", 7 | "placeholder": "Pegue la respuesta de la IA aquí..." 8 | }, 9 | "actions": { 10 | "cancel": "Cancelar", 11 | "submit": "Enviar" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/stories/Progress.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react" 2 | 3 | import { Progress } from "@/components/ui" 4 | 5 | const meta: Meta = { 6 | title: "Primitives/Progress", 7 | component: Progress, 8 | parameters: { 9 | layout: "centered", 10 | }, 11 | args: { 12 | className: "w-[300px]", 13 | }, 14 | tags: ["autodocs"], 15 | } 16 | 17 | export default meta 18 | 19 | type Story = StoryObj 20 | 21 | export const Default: Story = { 22 | args: { 23 | value: 50, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /src/schemas/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | // npx jest src/schemas/__tests__/index.test.ts 2 | 3 | import { GLOBAL_STATE_KEYS } from "../index" 4 | 5 | describe("GLOBAL_STATE_KEYS", () => { 6 | it("should contain provider settings keys", () => { 7 | expect(GLOBAL_STATE_KEYS).toContain("autoApprovalEnabled") 8 | }) 9 | 10 | it("should contain provider settings keys", () => { 11 | expect(GLOBAL_STATE_KEYS).toContain("anthropicBaseUrl") 12 | }) 13 | 14 | it("should not contain secret state keys", () => { 15 | expect(GLOBAL_STATE_KEYS).not.toContain("openRouterApiKey") 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": ["@typescript-eslint"], 9 | "rules": { 10 | "@typescript-eslint/naming-convention": [ 11 | "warn", 12 | { 13 | "selector": "import", 14 | "format": ["camelCase", "PascalCase"] 15 | } 16 | ], 17 | "@typescript-eslint/semi": "off", 18 | "eqeqeq": "warn", 19 | "no-throw-literal": "warn", 20 | "semi": "off" 21 | }, 22 | "ignorePatterns": ["out", "dist", "**/*.d.ts", "!roo-code.d.ts"] 23 | } 24 | -------------------------------------------------------------------------------- /src/__mocks__/serialize-error.js: -------------------------------------------------------------------------------- 1 | function serializeError(error) { 2 | if (error instanceof Error) { 3 | return { 4 | name: error.name, 5 | message: error.message, 6 | stack: error.stack, 7 | } 8 | } 9 | return error 10 | } 11 | 12 | function deserializeError(errorData) { 13 | if (errorData && typeof errorData === "object") { 14 | const error = new Error(errorData.message) 15 | error.name = errorData.name 16 | error.stack = errorData.stack 17 | return error 18 | } 19 | return errorData 20 | } 21 | 22 | module.exports = { 23 | serializeError, 24 | deserializeError, 25 | } 26 | -------------------------------------------------------------------------------- /src/core/webview/getNonce.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A helper function that returns a unique alphanumeric identifier called a nonce. 3 | * 4 | * @remarks This function is primarily used to help enforce content security 5 | * policies for resources/scripts being executed in a webview context. 6 | * 7 | * @returns A nonce 8 | */ 9 | export function getNonce() { 10 | let text = "" 11 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 12 | for (let i = 0; i < 32; i++) { 13 | text += possible.charAt(Math.floor(Math.random() * possible.length)) 14 | } 15 | return text 16 | } 17 | -------------------------------------------------------------------------------- /webview-ui/src/components/scheduler/LabeledInput.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Input } from "../../components/ui/input"; 3 | 4 | const LabeledInput = ({ 5 | label, 6 | required, 7 | ...props 8 | }: { label: string; required?: boolean } & React.ComponentProps) => ( 9 |
10 | 14 | 15 |
16 | ); 17 | 18 | export default LabeledInput; -------------------------------------------------------------------------------- /webview-ui/src/components/ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./alert-dialog" 2 | export * from "./autosize-textarea" 3 | export * from "./badge" 4 | export * from "./button" 5 | export * from "./checkbox" 6 | export * from "./collapsible" 7 | export * from "./command" 8 | export * from "./dialog" 9 | export * from "./dropdown-menu" 10 | export * from "./input" 11 | export * from "./popover" 12 | export * from "./progress" 13 | export * from "./separator" 14 | export * from "./slider" 15 | export * from "./select-dropdown" 16 | export * from "./select" 17 | export * from "./textarea" 18 | export * from "./tooltip" 19 | -------------------------------------------------------------------------------- /e2e/.vscode-test.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * See: https://code.visualstudio.com/api/working-with-extensions/testing-extension 3 | */ 4 | 5 | import { defineConfig } from '@vscode/test-cli'; 6 | 7 | export default defineConfig({ 8 | label: 'integrationTest', 9 | files: 'out/suite/**/*.test.js', 10 | workspaceFolder: '.', 11 | mocha: { 12 | ui: 'tdd', 13 | timeout: 60000, 14 | }, 15 | launchArgs: [ 16 | '--enable-proposed-api=RooVeterinaryInc.roo-cline', 17 | // Don't disable all extensions, as we need the Roo-cline extension to be available 18 | '--disable-extension=ms-vscode.vscode-typescript-next' 19 | ] 20 | }); 21 | -------------------------------------------------------------------------------- /src/__mocks__/p-wait-for.js: -------------------------------------------------------------------------------- 1 | function pWaitFor(condition, options = {}) { 2 | return new Promise((resolve, reject) => { 3 | let timeout 4 | 5 | const interval = setInterval(() => { 6 | if (condition()) { 7 | if (timeout) { 8 | clearTimeout(timeout) 9 | } 10 | 11 | clearInterval(interval) 12 | resolve() 13 | } 14 | }, options.interval || 20) 15 | 16 | if (options.timeout) { 17 | timeout = setTimeout(() => { 18 | clearInterval(interval) 19 | reject(new Error("Timed out")) 20 | }, options.timeout) 21 | } 22 | }) 23 | } 24 | 25 | module.exports = pWaitFor 26 | module.exports.default = pWaitFor 27 | -------------------------------------------------------------------------------- /webview-ui/src/stories/AutosizeTextarea.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react" 2 | import { AutosizeTextarea } from "../components/ui/autosize-textarea" 3 | 4 | const meta: Meta = { 5 | title: "Primitives/AutosizeTextarea", 6 | component: AutosizeTextarea, 7 | tags: ["autodocs"], 8 | args: { 9 | minHeight: 40, 10 | maxHeight: 400, 11 | placeholder: "This textarea will expand as you type.", 12 | className: "p-2", 13 | }, 14 | } 15 | 16 | export default meta 17 | 18 | type Story = StoryObj 19 | 20 | export const Default: Story = { 21 | args: {}, 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false, // set this to true to hide the "out" folder with the compiled JS files 5 | "dist": false // set this to true to hide the "dist" folder with the compiled JS files 6 | }, 7 | "search.exclude": { 8 | "out": true, // set this to false to include "out" folder in search results 9 | "dist": true // set this to false to include "dist" folder in search results 10 | }, 11 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 12 | "typescript.tsc.autoDetect": "off" 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/hooks/useClipboard.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | 3 | export interface UseClipboardProps { 4 | timeout?: number 5 | } 6 | 7 | export function useClipboard({ timeout = 2000 }: UseClipboardProps = {}) { 8 | const [isCopied, setIsCopied] = useState(false) 9 | 10 | const copy = (value: string) => { 11 | if (typeof window === "undefined" || !navigator.clipboard?.writeText || !value) { 12 | return 13 | } 14 | 15 | navigator.clipboard.writeText(value).then(() => { 16 | setIsCopied(true) 17 | setTimeout(() => setIsCopied(false), timeout) 18 | }) 19 | } 20 | 21 | return { isCopied, copy } 22 | } 23 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | interface ScrollAreaProps extends React.HTMLAttributes { 4 | viewportClassName?: string 5 | } 6 | 7 | const ScrollArea = React.forwardRef( 8 | ({ className, children, viewportClassName, ...props }, ref) => ( 9 |
10 |
11 | {children} 12 |
13 |
14 | ) 15 | ) 16 | ScrollArea.displayName = "ScrollArea" 17 | 18 | export { ScrollArea } -------------------------------------------------------------------------------- /webview-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": ".", 19 | "paths": { 20 | "@/*": ["./src/*"] 21 | } 22 | }, 23 | "include": ["src", "../src/shared"] 24 | } 25 | -------------------------------------------------------------------------------- /webview-ui/src/stories/Slider.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react" 2 | 3 | import { Slider } from "@/components/ui" 4 | 5 | const meta: Meta = { 6 | title: "Primitives/Slider", 7 | component: Slider, 8 | parameters: { 9 | layout: "centered", 10 | }, 11 | args: { 12 | defaultValue: [50], 13 | max: 100, 14 | min: 0, 15 | step: 1, 16 | className: "w-[300px]", 17 | }, 18 | tags: ["autodocs"], 19 | } 20 | 21 | export default meta 22 | 23 | type Story = StoryObj 24 | 25 | export const Default: Story = { 26 | args: { 27 | defaultValue: [50], 28 | max: 100, 29 | min: 0, 30 | step: 1, 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1737569578, 6 | "narHash": "sha256-6qY0pk2QmUtBT9Mywdvif0i/CLVgpCjMUn6g9vB+f3M=", 7 | "owner": "nixos", 8 | "repo": "nixpkgs", 9 | "rev": "47addd76727f42d351590c905d9d1905ca895b82", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "nixos", 14 | "ref": "nixos-24.11", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Roo Code development environment"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; 6 | }; 7 | 8 | outputs = { self, nixpkgs, ... }: let 9 | systems = [ "aarch64-darwin" "x86_64-linux" ]; 10 | 11 | forAllSystems = nixpkgs.lib.genAttrs systems; 12 | 13 | mkDevShell = system: let 14 | pkgs = import nixpkgs { inherit system; }; 15 | in pkgs.mkShell { 16 | name = "roo-code"; 17 | 18 | packages = with pkgs; [ 19 | nodejs_20 20 | corepack_20 21 | ]; 22 | }; 23 | in { 24 | devShells = forAllSystems (system: { 25 | default = mkDevShell system; 26 | }); 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/__mocks__/@modelcontextprotocol/sdk/index.js: -------------------------------------------------------------------------------- 1 | const { Client } = require("./client/index.js") 2 | const { StdioClientTransport, StdioServerParameters } = require("./client/stdio.js") 3 | const { 4 | CallToolResultSchema, 5 | ListToolsResultSchema, 6 | ListResourcesResultSchema, 7 | ListResourceTemplatesResultSchema, 8 | ReadResourceResultSchema, 9 | ErrorCode, 10 | McpError, 11 | } = require("./types.js") 12 | 13 | module.exports = { 14 | Client, 15 | StdioClientTransport, 16 | StdioServerParameters, 17 | CallToolResultSchema, 18 | ListToolsResultSchema, 19 | ListResourcesResultSchema, 20 | ListResourceTemplatesResultSchema, 21 | ReadResourceResultSchema, 22 | ErrorCode, 23 | McpError, 24 | } 25 | -------------------------------------------------------------------------------- /PRIVACY.md: -------------------------------------------------------------------------------- 1 | # Roo Code Privacy Policy 2 | 3 | **Last Updated: April 22nd, 2025** 4 | 5 | Roo Code respects your privacy and is committed to transparency about how we handle your data. Below is a simple breakdown of where key pieces of data go—and, importantly, where they don’t. 6 | 7 | ### **Where Your Data Goes (And Where It Doesn’t)** 8 | 9 | - **Code & Files**: Roo Scheduler reads and writes .roo/schedules.json in your workspace 10 | 11 | - **Telemetry (Usage Data)**: We do not collect usage data in Roo Scheduler 12 | 13 | ### **Contact Us** 14 | 15 | For any privacy-related questions, reach out to us at support@kylehoskins.com. 16 | 17 | --- 18 | 19 | By using Roo Scheduler, you agree to this Privacy Policy. 20 | -------------------------------------------------------------------------------- /src/core/webview/getUri.ts: -------------------------------------------------------------------------------- 1 | import { Uri, Webview } from "vscode" 2 | /** 3 | * A helper function which will get the webview URI of a given file or resource. 4 | * 5 | * @remarks This URI can be used within a webview's HTML as a link to the 6 | * given file/resource. 7 | * 8 | * @param webview A reference to the extension webview 9 | * @param extensionUri The URI of the directory containing the extension 10 | * @param pathList An array of strings representing the path to a file/resource 11 | * @returns A URI pointing to the file/resource 12 | */ 13 | export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) { 14 | return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList)) 15 | } 16 | -------------------------------------------------------------------------------- /e2e/rooservice-test.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration for running RooService integration tests with the Roo-cline extension enabled. 3 | * See: https://code.visualstudio.com/api/working-with-extensions/testing-extension 4 | */ 5 | 6 | import { defineConfig } from '@vscode/test-cli'; 7 | 8 | export default defineConfig({ 9 | label: 'rooServiceTest', 10 | files: 'out/suite/rooService.test.js', 11 | workspaceFolder: '.', 12 | mocha: { 13 | ui: 'tdd', 14 | timeout: 60000, 15 | }, 16 | launchArgs: [ 17 | '--enable-proposed-api=RooVeterinaryInc.roo-cline', 18 | // Don't disable all extensions, as we need the Roo-cline extension to be available 19 | '--disable-extension=ms-vscode.vscode-typescript-next' 20 | ] 21 | }); -------------------------------------------------------------------------------- /src/shared/__tests__/language.test.ts: -------------------------------------------------------------------------------- 1 | // npx jest src/shared/__tests__/language.test.ts 2 | 3 | import { formatLanguage } from "../language" 4 | 5 | describe("formatLanguage", () => { 6 | it("should uppercase region code in locale string", () => { 7 | expect(formatLanguage("pt-br")).toBe("pt-BR") 8 | expect(formatLanguage("zh-cn")).toBe("zh-CN") 9 | }) 10 | 11 | it("should return original string if no region code present", () => { 12 | expect(formatLanguage("en")).toBe("en") 13 | expect(formatLanguage("fr")).toBe("fr") 14 | }) 15 | 16 | it("should handle empty or undefined input", () => { 17 | expect(formatLanguage("")).toBe("en") 18 | expect(formatLanguage(undefined as unknown as string)).toBe("en") 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /webview-ui/src/__mocks__/vscrui.ts: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export const Checkbox = ({ children, onChange }: any) => 4 | React.createElement("div", { "data-testid": "mock-checkbox", onClick: onChange }, children) 5 | 6 | export const Dropdown = ({ children, onChange }: any) => 7 | React.createElement("div", { "data-testid": "mock-dropdown", onClick: onChange }, children) 8 | 9 | export const Pane = ({ children }: any) => React.createElement("div", { "data-testid": "mock-pane" }, children) 10 | 11 | export const Button = ({ children, ...props }: any) => 12 | React.createElement("div", { "data-testid": "mock-button", ...props }, children) 13 | 14 | export type DropdownOption = { 15 | label: string 16 | value: string 17 | } 18 | -------------------------------------------------------------------------------- /webview-ui/src/oauth/urls.ts: -------------------------------------------------------------------------------- 1 | export function getCallbackUrl(provider: string, uriScheme?: string) { 2 | const callbackUrl = `${uriScheme || "vscode"}://rooveterinaryinc.roo-cline/${provider}` 3 | return encodeURIComponent(callbackUrl) 4 | } 5 | 6 | export function getGlamaAuthUrl(uriScheme?: string) { 7 | return `https://glama.ai/oauth/authorize?callback_url=${getCallbackUrl("glama", uriScheme)}` 8 | } 9 | 10 | export function getOpenRouterAuthUrl(uriScheme?: string) { 11 | return `https://openrouter.ai/auth?callback_url=${getCallbackUrl("openrouter", uriScheme)}` 12 | } 13 | 14 | export function getRequestyAuthUrl(uriScheme?: string) { 15 | return `https://app.requesty.ai/oauth/authorize?callback_url=${getCallbackUrl("requesty", uriScheme)}` 16 | } 17 | -------------------------------------------------------------------------------- /knip.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/knip@latest/schema.json", 3 | "entry": ["src/extension.ts", "src/activate/index.ts", "webview-ui/src/index.tsx"], 4 | "project": ["src/**/*.ts", "webview-ui/src/**/*.{ts,tsx}"], 5 | "ignore": [ 6 | "**/__mocks__/**", 7 | "**/__tests__/**", 8 | "**/test/**", 9 | "**/*.test.ts", 10 | "**/*.test.tsx", 11 | "**/stories/**", 12 | "coverage/**", 13 | "dist/**", 14 | "out/**", 15 | "bin/**", 16 | "e2e/**", 17 | "evals/**", 18 | "src/activate/**", 19 | "src/exports/**", 20 | "src/schemas/ipc.ts", 21 | "src/extension.ts", 22 | "scripts/**" 23 | ], 24 | "workspaces": { 25 | "webview-ui": { 26 | "entry": ["src/index.tsx"], 27 | "project": ["src/**/*.{ts,tsx}"] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/utils/logging/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Main entry point for the compact logging system 3 | * Provides a default logger instance with Jest environment detection 4 | */ 5 | 6 | import { CompactLogger } from "./CompactLogger" 7 | 8 | /** 9 | * No-operation logger implementation for production environments 10 | */ 11 | const noopLogger = { 12 | debug: () => {}, 13 | info: () => {}, 14 | warn: () => {}, 15 | error: () => {}, 16 | fatal: () => {}, 17 | child: () => noopLogger, 18 | close: () => {}, 19 | } 20 | 21 | /** 22 | * Default logger instance 23 | * Uses CompactLogger for normal operation, switches to noop logger in Jest test environment 24 | */ 25 | export const logger = process.env.JEST_WORKER_ID !== undefined ? new CompactLogger() : noopLogger 26 | -------------------------------------------------------------------------------- /.roo/rules-translate/instructions-zh-tw.md: -------------------------------------------------------------------------------- 1 | # Traditional Chinese (zh-TW) Translation Guidelines 2 | 3 | ## Key Terminology 4 | 5 | | English Term | Use (zh-TW) | Avoid (Mainland) | 6 | | ------------- | ----------- | ---------------- | 7 | | file | 檔案 | 文件 | 8 | | task | 工作 | 任務 | 9 | | project | 專案 | 項目 | 10 | | configuration | 設定 | 配置 | 11 | | server | 伺服器 | 服務器 | 12 | | import/export | 匯入/匯出 | 導入/導出 | 13 | 14 | ## Formatting Rules 15 | 16 | - Add spaces between Chinese and English/numbers: "AI 驅動" (not "AI驅動") 17 | - Use Traditional Chinese quotation marks: 「範例文字」(not "範例文字") 18 | - Use Taiwanese computing conventions rather than mainland terminology 19 | -------------------------------------------------------------------------------- /.roo/rules/rules.md: -------------------------------------------------------------------------------- 1 | # Code Quality Rules 2 | 3 | 1. Test Coverage: 4 | 5 | - Before attempting completion, always make sure that any code changes have test coverage 6 | - Ensure all tests pass before submitting changes 7 | 8 | 2. Lint Rules: 9 | 10 | - Never disable any lint rules without explicit user approval 11 | 12 | 3. Styling Guidelines: 13 | - Use Tailwind CSS classes instead of inline style objects for new markup 14 | - VSCode CSS variables must be added to webview-ui/src/index.css before using them in Tailwind classes 15 | - Example: `
` instead of style objects 16 | 17 | # Adding a New Setting 18 | 19 | To add a new setting that persists its state, follow the steps in cline_docs/settings.md 20 | -------------------------------------------------------------------------------- /webview-ui/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom" 2 | 3 | // Mock crypto.getRandomValues 4 | Object.defineProperty(window, "crypto", { 5 | value: { 6 | getRandomValues: function (buffer: Uint8Array) { 7 | for (let i = 0; i < buffer.length; i++) { 8 | buffer[i] = Math.floor(Math.random() * 256) 9 | } 10 | return buffer 11 | }, 12 | }, 13 | }) 14 | 15 | // Mock matchMedia 16 | Object.defineProperty(window, "matchMedia", { 17 | writable: true, 18 | value: jest.fn().mockImplementation((query) => ({ 19 | matches: false, 20 | media: query, 21 | onchange: null, 22 | addListener: jest.fn(), // deprecated 23 | removeListener: jest.fn(), // deprecated 24 | addEventListener: jest.fn(), 25 | removeEventListener: jest.fn(), 26 | dispatchEvent: jest.fn(), 27 | })), 28 | }) 29 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "experimentalDecorators": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "isolatedModules": true, 7 | "lib": ["es2022", "esnext.disposable", "DOM"], 8 | "module": "esnext", 9 | "moduleResolution": "Bundler", 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitOverride": true, 12 | "noImplicitReturns": true, 13 | "noUnusedLocals": false, 14 | "resolveJsonModule": true, 15 | "rootDir": ".", 16 | "skipLibCheck": true, 17 | "sourceMap": true, 18 | "strict": true, 19 | "target": "es2022", 20 | "useDefineForClassFields": true, 21 | "useUnknownInCatchVariables": false 22 | }, 23 | "include": ["src/**/*", "scripts/**/*", ".changeset/**/*"], 24 | "exclude": ["node_modules", ".vscode-test", "webview-ui"] 25 | } 26 | -------------------------------------------------------------------------------- /.roo/rules-translate/instructions-de.md: -------------------------------------------------------------------------------- 1 | # German (de) Translation Guidelines 2 | 3 | **Key Rule:** Always use informal speech ("du" form) in all German translations without exception. 4 | 5 | ## Quick Reference 6 | 7 | | Category | Formal (Avoid) | Informal (Use) | Example | 8 | | ----------- | ------------------------- | ------------------- | ----------------- | 9 | | Pronouns | Sie | du | you | 10 | | Possessives | Ihr/Ihre/Ihrem | dein/deine/deinem | your | 11 | | Verbs | können Sie, müssen Sie | kannst du, musst du | you can, you must | 12 | | Imperatives | Geben Sie ein, Wählen Sie | Gib ein, Wähle | Enter, Choose | 13 | 14 | **Technical terms** like "API", "token", "prompt" should not be translated. 15 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as SeparatorPrimitive from "@radix-ui/react-separator" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const Separator = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => ( 10 | 21 | )) 22 | Separator.displayName = SeparatorPrimitive.Root.displayName 23 | 24 | export { Separator } 25 | -------------------------------------------------------------------------------- /src/utils/logging/__tests__/MockTransport.ts: -------------------------------------------------------------------------------- 1 | // __tests__/MockTransport.ts 2 | import { CompactTransport } from "../CompactTransport" 3 | import type { CompactLogEntry, CompactTransportConfig } from "../types" 4 | 5 | const TEST_CONFIG: CompactTransportConfig = { 6 | level: "fatal", 7 | fileOutput: { 8 | enabled: false, 9 | path: "", 10 | }, 11 | } 12 | 13 | export class MockTransport extends CompactTransport { 14 | public entries: CompactLogEntry[] = [] 15 | public closed = false 16 | 17 | constructor() { 18 | super(TEST_CONFIG) 19 | } 20 | 21 | override async write(entry: CompactLogEntry): Promise { 22 | this.entries.push(entry) 23 | } 24 | 25 | override async close(): Promise { 26 | this.closed = true 27 | await super.close() 28 | } 29 | 30 | clear(): void { 31 | this.entries = [] 32 | this.closed = false 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/zh-TW/welcome.json: -------------------------------------------------------------------------------- 1 | { 2 | "greeting": "嗨,我是 Roo!", 3 | "introduction": "由於程式代理功能的最新突破,以及能夠讓我建立和編輯檔案、探索複雜專案、使用瀏覽器和執行終端機命令的工具(在您授權後),我能完成各種工作。我甚至可以使用 MCP 建立新工具並擴展自己的功能。", 4 | "notice": "要開始使用,此擴充功能需要一個 API 提供者。", 5 | "start": "讓我們開始吧!", 6 | "chooseProvider": "選擇一個 API 提供者開始:", 7 | "routers": { 8 | "requesty": { 9 | "description": "您的最佳化 LLM 路由器", 10 | "incentive": "$1 免費額度" 11 | }, 12 | "openrouter": { 13 | "description": "LLM 的統一介面" 14 | } 15 | }, 16 | "startRouter": "透過路由器快速設定", 17 | "startCustom": "使用您自己的 API 金鑰", 18 | "telemetry": { 19 | "title": "協助改進 Roo Code", 20 | "anonymousTelemetry": "傳送匿名的錯誤和使用資料,以協助我們修復錯誤並改進擴充功能。我們絕不會傳送任何程式碼、提示或個人資訊。", 21 | "changeSettings": "您隨時可以在設定底端更改此選項", 22 | "settings": "設定", 23 | "allow": "允許", 24 | "deny": "拒絕" 25 | }, 26 | "or": "或是" 27 | } 28 | -------------------------------------------------------------------------------- /e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e2e", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "lint": "eslint src --ext ts", 7 | "check-types": "tsc --noEmit", 8 | "test": "npm run build && node ./out/e2e/src/runTest.js", 9 | "test:rooservice": "npm run build && npx @vscode/test-cli --config rooservice-test.mjs", 10 | "test:all": "npm run test && npm run test:rooservice", 11 | "ci": "npm run vscode-test && npm run test:all", 12 | "build": "rimraf out && tsc -p tsconfig.json", 13 | "clean": "rimraf out", 14 | "vscode-test": "cd .. && npm run vscode-test" 15 | }, 16 | "dependencies": {}, 17 | "devDependencies": { 18 | "@types/mocha": "^10.0.10", 19 | "@vscode/test-cli": "^0.0.9", 20 | "@vscode/test-electron": "^2.4.0", 21 | "glob": "^10.3.10", 22 | "mocha": "^11.1.0", 23 | "rimraf": "^5.0.5", 24 | "typescript": "^5.4.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/locales/zh-CN/welcome.json: -------------------------------------------------------------------------------- 1 | { 2 | "greeting": "你好,我是 Roo!", 3 | "introduction": "基于最新的AI编程技术,我可以逐步处理复杂软件开发任务。支持创建编辑文件、分析复杂项目、浏览器操作及运行终端命令,不仅能提供代码补全和基础答疑,还能完成更高阶的开发协助。通过MCP系统,我甚至可以自己制作新工具,持续提升解决问题的能力。", 4 | "notice": "请先配置大语言模型API提供商", 5 | "start": "开始吧!", 6 | "chooseProvider": "选择一个 API 提供商开始:", 7 | "routers": { 8 | "requesty": { 9 | "description": "智能调度多个大语言模型", 10 | "incentive": "$1 免费额度" 11 | }, 12 | "openrouter": { 13 | "description": "统一了大语言模型的接口" 14 | } 15 | }, 16 | "startRouter": "通过路由器快速设置", 17 | "startCustom": "使用你自己的 API 密钥", 18 | "telemetry": { 19 | "title": "帮助改进 Roo Code", 20 | "changeSettings": "可以随时在设置页面底部更改此设置", 21 | "settings": "设置", 22 | "anonymousTelemetry": "发送匿名的错误和使用数据,以帮助我们修复错误并改进扩展程序。不会涉及代码、提示词或个人隐私信息。", 23 | "allow": "允许", 24 | "deny": "拒绝" 25 | }, 26 | "or": "或" 27 | } 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.0.11] - 2025-05-31 4 | 5 | ### Added 6 | - Upgraded to Roo Code's latest custom_modes.yaml support 7 | 8 | ## [0.0.10] - 2025-04-25 9 | 10 | ### Fixed 11 | - Resolved an issue where `startDate` was set by default. 12 | 13 | ### Changed 14 | - Updated scheduling logic for interval-based tasks: 15 | - **If a start date/time is specified:** Intervals are now calculated from the original start time. For example, for an hourly task with a start time of 10:00am, if execution is delayed (e.g., due to inactivity or the computer being off/in deep sleep) and the task runs at 10:15am, the next execution is scheduled for 11:00am. 16 | - **If no start time is specified:** The interval is calculated from the last execution time. For example, if the last execution was at 10:15am, the next execution will be at 11:15am. 17 | - Updated "Usage Tips" in the README -------------------------------------------------------------------------------- /webview-ui/src/stories/Badge.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react" 2 | 3 | import { Badge } from "@/components/ui" 4 | 5 | const meta: Meta = { 6 | title: "Primitives/Badge", 7 | component: Badge, 8 | tags: ["autodocs"], 9 | args: { 10 | children: "Badge", 11 | }, 12 | } 13 | 14 | export default meta 15 | 16 | type Story = StoryObj 17 | 18 | export const Default: Story = { 19 | args: { 20 | children: "Default", 21 | }, 22 | } 23 | 24 | export const Secondary: Story = { 25 | args: { 26 | variant: "secondary", 27 | children: "Secondary", 28 | }, 29 | } 30 | 31 | export const Destructive: Story = { 32 | args: { 33 | variant: "destructive", 34 | children: "Destructive", 35 | }, 36 | } 37 | 38 | export const Outline: Story = { 39 | args: { 40 | variant: "outline", 41 | children: "Outline", 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Input = React.forwardRef>( 6 | ({ className, type, ...props }, ref) => { 7 | return ( 8 | 17 | ) 18 | }, 19 | ) 20 | Input.displayName = "Input" 21 | 22 | export { Input } 23 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/progress.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as ProgressPrimitive from "@radix-ui/react-progress" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const Progress = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, value, ...props }, ref) => ( 10 | 14 | 18 | 19 | )) 20 | Progress.displayName = ProgressPrimitive.Root.displayName 21 | 22 | export { Progress } 23 | -------------------------------------------------------------------------------- /webview-ui/src/i18n/test-utils.ts: -------------------------------------------------------------------------------- 1 | import i18next from "i18next" 2 | import { initReactI18next } from "react-i18next" 3 | 4 | /** 5 | * Sets up i18next for testing with pre-defined translations. 6 | * Use this in test files to ensure consistent translation handling. 7 | */ 8 | export const setupI18nForTests = () => { 9 | i18next.use(initReactI18next).init({ 10 | lng: "en", 11 | fallbackLng: "en", 12 | debug: false, 13 | interpolation: { 14 | escapeValue: false, 15 | }, 16 | // Pre-define all translations needed for tests 17 | resources: { 18 | en: { 19 | settings: { 20 | autoApprove: { 21 | title: "Auto-Approve", 22 | }, 23 | }, 24 | common: { 25 | notifications: { 26 | error: "Operation failed: {{message}}", 27 | }, 28 | }, 29 | chat: { 30 | test: "Test", 31 | }, 32 | }, 33 | }, 34 | }) 35 | 36 | return i18next 37 | } 38 | -------------------------------------------------------------------------------- /webview-ui/src/utils/TelemetryClient.ts: -------------------------------------------------------------------------------- 1 | import posthog from "posthog-js" 2 | 3 | class TelemetryClient { 4 | private static instance: TelemetryClient 5 | private static telemetryEnabled: boolean = false 6 | 7 | public updateTelemetryState(telemetrySetting: any, apiKey?: string, distinctId?: string) { 8 | 9 | } 10 | 11 | public static getInstance(): TelemetryClient { 12 | if (!TelemetryClient.instance) { 13 | TelemetryClient.instance = new TelemetryClient() 14 | } 15 | return TelemetryClient.instance 16 | } 17 | 18 | public capture(eventName: string, properties?: Record) { 19 | if (TelemetryClient.telemetryEnabled) { 20 | try { 21 | posthog.capture(eventName, properties) 22 | } catch (error) { 23 | // Silently fail if there's an error capturing an event 24 | } 25 | } 26 | } 27 | } 28 | 29 | export const telemetryClient = TelemetryClient.getInstance() 30 | -------------------------------------------------------------------------------- /webview-ui/src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Textarea = React.forwardRef>( 6 | ({ className, ...props }, ref) => { 7 | return ( 8 |