From 48d2411a118f5aaa4e5b7e325f8e71659f691a07 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Sun, 11 Aug 2024 17:14:05 -0400 Subject: [PATCH] Add option to set custom instructions --- CHANGELOG.md | 3 ++- src/ClaudeDev.ts | 21 ++++++++++++++- src/providers/ClaudeDevProvider.ts | 25 +++++++++++++++--- src/shared/ExtensionMessage.ts | 1 + src/shared/WebviewMessage.ts | 1 + webview-ui/src/App.tsx | 4 +++ webview-ui/src/components/Announcement.tsx | 4 +++ webview-ui/src/components/SettingsView.tsx | 30 ++++++++++++++++++++-- 8 files changed, 81 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb5ece2..ac0ca40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ All notable changes to the "claude-dev" extension will be documented in this fil ## [1.1.1] -- Added the ability to choose other Claude models (+ GPT-4o, DeepSeek, and Mistral if you use OpenRouter) +- Adds option to choose other Claude models (+ GPT-4o, DeepSeek, and Mistral if you use OpenRouter) +- Adds option to add custom instructions to the end of the system prompt ## [1.1.0] diff --git a/src/ClaudeDev.ts b/src/ClaudeDev.ts index 1a72bfa..4b09562 100644 --- a/src/ClaudeDev.ts +++ b/src/ClaudeDev.ts @@ -235,6 +235,7 @@ type ToolResponse = string | Array { try { - return await this.api.createMessage(SYSTEM_PROMPT(), this.apiConversationHistory, tools) + let systemPrompt = SYSTEM_PROMPT() + if (this.customInstructions && this.customInstructions.trim()) { + systemPrompt += ` +==== + +USER'S CUSTOM INSTRUCTIONS + +The following additional instructions are provided by the user. They should be followed and given precedence in case of conflicts with previous instructions. + +${this.customInstructions.trim()} +` + } + return await this.api.createMessage(systemPrompt, this.apiConversationHistory, tools) } catch (error) { const { response } = await this.ask( "api_req_failed", diff --git a/src/providers/ClaudeDevProvider.ts b/src/providers/ClaudeDevProvider.ts index 5a45caf..e102eb6 100644 --- a/src/providers/ClaudeDevProvider.ts +++ b/src/providers/ClaudeDevProvider.ts @@ -11,7 +11,13 @@ https://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/c */ type SecretKey = "apiKey" | "openRouterApiKey" | "awsAccessKey" | "awsSecretKey" -type GlobalStateKey = "apiProvider" | "apiModelId" | "awsRegion" | "maxRequestsPerTask" | "lastShownAnnouncementId" +type GlobalStateKey = + | "apiProvider" + | "apiModelId" + | "awsRegion" + | "maxRequestsPerTask" + | "lastShownAnnouncementId" + | "customInstructions" export class ClaudeDevProvider implements vscode.WebviewViewProvider { public static readonly sideBarId = "claude-dev.SidebarProvider" // used in package.json as the view's id. This value cannot be changed due to how vscode caches views based on their id, and updating the id would break existing instances of the extension. @@ -132,8 +138,8 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { async initClaudeDevWithTask(task?: string, images?: string[]) { await this.clearTask() // ensures that an exising task doesn't exist before starting a new one, although this shouldn't be possible since user must clear task before starting a new one - const { maxRequestsPerTask, apiConfiguration } = await this.getState() - this.claudeDev = new ClaudeDev(this, apiConfiguration, maxRequestsPerTask, task, images) + const { maxRequestsPerTask, apiConfiguration, customInstructions } = await this.getState() + this.claudeDev = new ClaudeDev(this, apiConfiguration, maxRequestsPerTask, customInstructions, task, images) } // Send any JSON serializable data to the react app @@ -280,6 +286,12 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { this.claudeDev?.updateMaxRequestsPerTask(result) await this.postStateToWebview() break + case "customInstructions": + // User may be clearing the field + await this.updateGlobalState("customInstructions", message.text || undefined) + this.claudeDev?.updateCustomInstructions(message.text || undefined) + await this.postStateToWebview() + break case "askResponse": this.claudeDev?.handleWebviewAskResponse(message.askResponse!, message.text, message.images) break @@ -309,13 +321,15 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { } async postStateToWebview() { - const { apiConfiguration, maxRequestsPerTask, lastShownAnnouncementId } = await this.getState() + const { apiConfiguration, maxRequestsPerTask, lastShownAnnouncementId, customInstructions } = + await this.getState() this.postMessageToWebview({ type: "state", state: { version: this.context.extension?.packageJSON?.version ?? "", apiConfiguration, maxRequestsPerTask, + customInstructions, themeName: vscode.workspace.getConfiguration("workbench").get("colorTheme"), claudeMessages: this.claudeDev?.claudeMessages || [], shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId, @@ -420,6 +434,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { awsRegion, maxRequestsPerTask, lastShownAnnouncementId, + customInstructions, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, this.getGlobalState("apiModelId") as Promise, @@ -430,6 +445,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { this.getGlobalState("awsRegion") as Promise, this.getGlobalState("maxRequestsPerTask") as Promise, this.getGlobalState("lastShownAnnouncementId") as Promise, + this.getGlobalState("customInstructions") as Promise, ]) return { apiConfiguration: { @@ -443,6 +459,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { }, maxRequestsPerTask, lastShownAnnouncementId, + customInstructions, } } diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 0ac3d10..2527ff7 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -15,6 +15,7 @@ export interface ExtensionState { version: string apiConfiguration?: ApiConfiguration maxRequestsPerTask?: number + customInstructions?: string themeName?: string claudeMessages: ClaudeMessage[] shouldShowAnnouncement: boolean diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index d8dee18..86e0aca 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -4,6 +4,7 @@ export interface WebviewMessage { type: | "apiConfiguration" | "maxRequestsPerTask" + | "customInstructions" | "webviewDidLaunch" | "newTask" | "askResponse" diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 74ed94a..f8cbd30 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -22,6 +22,7 @@ const App: React.FC = () => { const [version, setVersion] = useState("") const [apiConfiguration, setApiConfiguration] = useState(undefined) const [maxRequestsPerTask, setMaxRequestsPerTask] = useState("") + const [customInstructions, setCustomInstructions] = useState("") const [vscodeThemeName, setVscodeThemeName] = useState(undefined) const [claudeMessages, setClaudeMessages] = useState([]) const [showAnnouncement, setShowAnnouncement] = useState(false) @@ -44,6 +45,7 @@ const App: React.FC = () => { setMaxRequestsPerTask( message.state!.maxRequestsPerTask !== undefined ? message.state!.maxRequestsPerTask.toString() : "" ) + setCustomInstructions(message.state!.customInstructions || "") setVscodeThemeName(message.state!.themeName) setClaudeMessages(message.state!.claudeMessages) // don't update showAnnouncement to false if shouldShowAnnouncement is false @@ -90,6 +92,8 @@ const App: React.FC = () => { setApiConfiguration={setApiConfiguration} maxRequestsPerTask={maxRequestsPerTask} setMaxRequestsPerTask={setMaxRequestsPerTask} + customInstructions={customInstructions} + setCustomInstructions={setCustomInstructions} onDone={() => setShowSettings(false)} /> )} diff --git a/webview-ui/src/components/Announcement.tsx b/webview-ui/src/components/Announcement.tsx index 4e5d759..ece8654 100644 --- a/webview-ui/src/components/Announcement.tsx +++ b/webview-ui/src/components/Announcement.tsx @@ -35,6 +35,10 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => { Added a settings option to choose other Claude models (+ GPT-4o, DeepSeek, and Mistral if you use OpenRouter) +
  • + You can now add custom instructions to the end of the system prompt (e.g. "Always use Python", + "Speak in Spanish") +
  • Improved support for running interactive terminal commands and long-running processes like servers
  • diff --git a/webview-ui/src/components/SettingsView.tsx b/webview-ui/src/components/SettingsView.tsx index 32199ab..37d44bc 100644 --- a/webview-ui/src/components/SettingsView.tsx +++ b/webview-ui/src/components/SettingsView.tsx @@ -1,4 +1,4 @@ -import { VSCodeButton, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import { VSCodeButton, VSCodeLink, VSCodeTextArea, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import React, { useEffect, useState } from "react" import { ApiConfiguration } from "../../../src/shared/api" import { validateApiConfiguration, validateMaxRequestsPerTask } from "../utils/validate" @@ -11,6 +11,8 @@ type SettingsViewProps = { setApiConfiguration: React.Dispatch> maxRequestsPerTask: string setMaxRequestsPerTask: React.Dispatch> + customInstructions: string + setCustomInstructions: React.Dispatch> onDone: () => void } @@ -20,6 +22,8 @@ const SettingsView = ({ setApiConfiguration, maxRequestsPerTask, setMaxRequestsPerTask, + customInstructions, + setCustomInstructions, onDone, }: SettingsViewProps) => { const [apiErrorMessage, setApiErrorMessage] = useState(undefined) @@ -35,6 +39,7 @@ const SettingsView = ({ if (!apiValidationResult && !maxRequestsValidationResult) { vscode.postMessage({ type: "apiConfiguration", apiConfiguration }) vscode.postMessage({ type: "maxRequestsPerTask", text: maxRequestsPerTask }) + vscode.postMessage({ type: "customInstructions", text: customInstructions }) onDone() } } @@ -102,7 +107,28 @@ const SettingsView = ({ )} -
    +
    + setCustomInstructions(e.target?.value || "")}> + Custom Instructions + +

    + These instructions are added to the end of the system prompt sent with every request. +

    +
    + +