Add a screen for custom prompts

This commit is contained in:
Matt Rubens
2025-01-13 03:16:10 -05:00
parent 4027e1c10c
commit 75e308b033
21 changed files with 1044 additions and 238 deletions

View File

@@ -29,100 +29,39 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
style={{ position: "absolute", top: "8px", right: "8px" }}>
<span className="codicon codicon-close"></span>
</VSCodeButton>
<h2 style={{ margin: "0 0 8px" }}>
🎉{" "}Introducing Roo Cline v{minorVersion}
</h2>
<h3 style={{ margin: "0 0 8px" }}>
🎉{" "}New in Cline v{minorVersion}
Agent Modes Customization
</h3>
<p style={{ margin: "5px 0px", fontWeight: "bold" }}>Add custom tools to Cline using MCP!</p>
<p style={{ margin: "5px 0px" }}>
The Model Context Protocol allows agents like Cline to plug and play custom tools,{" "}
<VSCodeLink href="https://github.com/modelcontextprotocol/servers" style={{ display: "inline" }}>
e.g. a web-search tool or GitHub tool.
</VSCodeLink>
</p>
<p style={{ margin: "5px 0px" }}>
You can add and configure MCP servers by clicking the new{" "}
<span className="codicon codicon-server" style={{ fontSize: "10px" }}></span> icon in the menu bar.
</p>
<p style={{ margin: "5px 0px" }}>
To take things a step further, Cline also has the ability to create custom tools for himself. Just say
"add a tool that..." and watch as he builds and installs new capabilities specific to{" "}
<i>your workflow</i>. For example:
Click the new <span className="codicon codicon-notebook" style={{ fontSize: "10px" }}></span> icon in the menu bar to open the Prompts Settings and customize Agent Modes for new levels of productivity.
<ul style={{ margin: "4px 0 6px 20px", padding: 0 }}>
<li>"...fetches Jira tickets": Get ticket ACs and put Cline to work</li>
<li>"...manages AWS EC2s": Check server metrics and scale up or down</li>
<li>"...pulls PagerDuty incidents": Pulls details to help Cline fix bugs</li>
<li>Tailor how Roo Cline behaves in different modes: Code, Architect, and Ask.</li>
<li>Preview and verify your changes using the Preview System Prompt button.</li>
</ul>
Cline handles everything from creating the MCP server to installing it in the extension, ready to use in
future tasks. The servers are saved to <code>~/Documents/Cline/MCP</code> so you can easily share them
with others too.{" "}
</p>
<h3 style={{ margin: "0 0 8px" }}>
Prompt Enhancement Configuration
</h3>
<p style={{ margin: "5px 0px" }}>
Try it yourself by asking Cline to "add a tool that gets the latest npm docs", or
<VSCodeLink href="https://x.com/sdrzn/status/1867271665086074969" style={{ display: "inline" }}>
see a demo of MCP in action here.
</VSCodeLink>
Now available for all providers! Access it directly in the chat box by clicking the <span className="codicon codicon-sparkle" style={{ fontSize: "10px" }}></span> sparkle icon next to the input field. From there, you can customize the enhancement logic and provider to best suit your workflow.
<ul style={{ margin: "4px 0 6px 20px", padding: 0 }}>
<li>Customize how prompts are enhanced for better results in your workflow.</li>
<li>Use the sparkle icon in the chat box to select a API configuration and provider (e.g., GPT-4) and configure your own enhancement logic.</li>
<li>Test your changes instantly with the Preview Prompt Enhancement tool.</li>
</ul>
</p>
{/*<ul style={{ margin: "0 0 8px", paddingLeft: "12px" }}>
<li>
OpenRouter now supports prompt caching! They also have much higher rate limits than other providers,
so I recommend trying them out.
<br />
{!apiConfiguration?.openRouterApiKey && (
<VSCodeButtonLink
href={getOpenRouterAuthUrl(vscodeUriScheme)}
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
margin: "4px -30px 2px 0",
}}>
Get OpenRouter API Key
</VSCodeButtonLink>
)}
{apiConfiguration?.openRouterApiKey && apiConfiguration?.apiProvider !== "openrouter" && (
<VSCodeButton
onClick={() => {
vscode.postMessage({
type: "apiConfiguration",
apiConfiguration: { ...apiConfiguration, apiProvider: "openrouter" },
})
}}
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
margin: "4px -30px 2px 0",
}}>
Switch to OpenRouter
</VSCodeButton>
)}
</li>
<li>
<b>Edit Cline's changes before accepting!</b> When he creates or edits a file, you can modify his
changes directly in the right side of the diff view (+ hover over the 'Revert Block' arrow button in
the center to undo "<code>{"// rest of code here"}</code>" shenanigans)
</li>
<li>
New <code>search_files</code> tool that lets Cline perform regex searches in your project, letting
him refactor code, address TODOs and FIXMEs, remove dead code, and more!
</li>
<li>
When Cline runs commands, you can now type directly in the terminal (+ support for Python
environments)
</li>
</ul>*/}
<div
style={{
height: "1px",
background: "var(--vscode-foreground)",
opacity: 0.1,
margin: "8px 0",
}}
/>
<p style={{ margin: "0" }}>
Join
<VSCodeLink style={{ display: "inline" }} href="https://discord.gg/cline">
discord.gg/cline
<p style={{ margin: "5px 0px" }}>
We're very excited to see what you build with this new feature! Join us at
<VSCodeLink href="https://www.reddit.com/r/roocline" style={{ display: "inline" }}>
reddit.com/r/roocline
</VSCodeLink>
for more updates!
to discuss and share feedback.
</p>
</div>
)

View File

@@ -49,7 +49,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
},
ref,
) => {
const { filePaths, apiConfiguration, currentApiConfigName, listApiConfigMeta } = useExtensionState()
const { filePaths, currentApiConfigName, listApiConfigMeta } = useExtensionState()
const [isTextAreaFocused, setIsTextAreaFocused] = useState(false)
const [gitCommits, setGitCommits] = useState<any[]>([])
const [showDropdown, setShowDropdown] = useState(false)
@@ -69,8 +69,10 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
useEffect(() => {
const messageHandler = (event: MessageEvent) => {
const message = event.data
if (message.type === 'enhancedPrompt' && message.text) {
setInputValue(message.text)
if (message.type === 'enhancedPrompt') {
if (message.text) {
setInputValue(message.text)
}
setIsEnhancingPrompt(false)
} else if (message.type === 'commitSearchResults') {
const commits = message.commits.map((commit: any) => ({
@@ -767,19 +769,25 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
</div>
<div className="button-row" style={{ position: "absolute", right: 16, display: "flex", alignItems: "center", height: 31, bottom: 11, zIndex: 3, padding: "0 8px", justifyContent: "flex-end", backgroundColor: "var(--vscode-input-background)", }}>
<span style={{ display: "flex", alignItems: "center", gap: 12 }}>
{apiConfiguration?.apiProvider === "openrouter" && (
<div style={{ display: "flex", alignItems: "center" }}>
{isEnhancingPrompt && <span style={{ marginRight: 10, color: "var(--vscode-input-foreground)", opacity: 0.5 }}>Enhancing prompt...</span>}
<span
role="button"
aria-label="enhance prompt"
data-testid="enhance-prompt-button"
className={`input-icon-button ${textAreaDisabled ? "disabled" : ""} codicon codicon-sparkle`}
onClick={() => !textAreaDisabled && handleEnhancePrompt()}
style={{ fontSize: 16.5 }}
/>
</div>
)}
<div style={{ display: "flex", alignItems: "center" }}>
{isEnhancingPrompt ? (
<span className="codicon codicon-loading codicon-modifier-spin" style={{
color: "var(--vscode-input-foreground)",
opacity: 0.5,
fontSize: 16.5,
marginRight: 10
}}></span>
) : (
<span
role="button"
aria-label="enhance prompt"
data-testid="enhance-prompt-button"
className={`input-icon-button ${textAreaDisabled ? "disabled" : ""} codicon codicon-sparkle`}
onClick={() => !textAreaDisabled && handleEnhancePrompt()}
style={{ fontSize: 16.5 }}
/>
)}
</div>
<span className={`input-icon-button ${shouldDisableImages ? "disabled" : ""} codicon codicon-device-camera`} onClick={() => !shouldDisableImages && onSelectImages()} style={{ fontSize: 16.5 }} />
<span className={`input-icon-button ${textAreaDisabled ? "disabled" : ""} codicon codicon-send`} onClick={() => !textAreaDisabled && onSend()} style={{ fontSize: 15 }} />
</span>

View File

@@ -3,6 +3,7 @@ import '@testing-library/jest-dom';
import ChatTextArea from '../ChatTextArea';
import { useExtensionState } from '../../../context/ExtensionStateContext';
import { vscode } from '../../../utils/vscode';
import { codeMode } from '../../../../../src/shared/modes';
// Mock modules
jest.mock('../../../utils/vscode', () => ({
@@ -32,6 +33,8 @@ describe('ChatTextArea', () => {
selectedImages: [],
setSelectedImages: jest.fn(),
onHeightChange: jest.fn(),
mode: codeMode,
setMode: jest.fn(),
};
beforeEach(() => {
@@ -46,37 +49,9 @@ describe('ChatTextArea', () => {
});
describe('enhance prompt button', () => {
it('should show enhance prompt button only when apiProvider is openrouter', () => {
// Test with non-openrouter provider
(useExtensionState as jest.Mock).mockReturnValue({
filePaths: [],
apiConfiguration: {
apiProvider: 'anthropic',
},
});
const { rerender } = render(<ChatTextArea {...defaultProps} />);
expect(screen.queryByTestId('enhance-prompt-button')).not.toBeInTheDocument();
// Test with openrouter provider
(useExtensionState as jest.Mock).mockReturnValue({
filePaths: [],
apiConfiguration: {
apiProvider: 'openrouter',
},
});
rerender(<ChatTextArea {...defaultProps} />);
const enhanceButton = screen.getByRole('button', { name: /enhance prompt/i });
expect(enhanceButton).toBeInTheDocument();
});
it('should be disabled when textAreaDisabled is true', () => {
(useExtensionState as jest.Mock).mockReturnValue({
filePaths: [],
apiConfiguration: {
apiProvider: 'openrouter',
},
});
render(<ChatTextArea {...defaultProps} textAreaDisabled={true} />);
@@ -137,7 +112,8 @@ describe('ChatTextArea', () => {
const enhanceButton = screen.getByRole('button', { name: /enhance prompt/i });
fireEvent.click(enhanceButton);
expect(screen.getByText('Enhancing prompt...')).toBeInTheDocument();
const loadingSpinner = screen.getByText('', { selector: '.codicon-loading' });
expect(loadingSpinner).toBeInTheDocument();
});
});