mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Add API for other extensions to interact with Claude Dev
This commit is contained in:
55
src/extension-api/README.md
Normal file
55
src/extension-api/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Claude Dev API
|
||||
|
||||
The Claude Dev extension exposes an API that can be used by other extensions. To use this API in your extension:
|
||||
|
||||
1. Copy `src/extension-api/claude-dev.d.ts` to your extension's source directory.
|
||||
2. Include `claude-dev.d.ts` in your extension's compilation.
|
||||
3. Get access to the API with the following code:
|
||||
|
||||
```ts
|
||||
const claudeDevExtension = vscode.extensions.getExtension<ClaudeDevAPI>("saoudrizwan.claude-dev")
|
||||
|
||||
if (!claudeDevExtension?.isActive) {
|
||||
throw new Error("Claude Dev extension is not activated")
|
||||
}
|
||||
|
||||
const claudeDev = claudeDevExtension.exports
|
||||
|
||||
if (claudeDev) {
|
||||
// Now you can use the API
|
||||
|
||||
// Set custom instructions
|
||||
await claudeDev.setCustomInstructions("Talk like a pirate")
|
||||
|
||||
// Get custom instructions
|
||||
const instructions = await claudeDev.getCustomInstructions()
|
||||
console.log("Current custom instructions:", instructions)
|
||||
|
||||
// Start a new task with an initial message
|
||||
await claudeDev.startNewTask("Hello, Claude! Let's make a new project...")
|
||||
|
||||
// Start a new task with an initial message and images
|
||||
await claudeDev.startNewTask("Use this design language", ["data:image/webp;base64,..."])
|
||||
|
||||
// Send a message to the current task
|
||||
await claudeDev.sendMessage("Can you fix the @problems?")
|
||||
|
||||
// Simulate pressing the primary button in the chat interface (e.g. 'Save' or 'Proceed While Running')
|
||||
await claudeDev.pressPrimaryButton()
|
||||
|
||||
// Simulate pressing the secondary button in the chat interface (e.g. 'Reject')
|
||||
await claudeDev.pressSecondaryButton()
|
||||
} else {
|
||||
console.error("Claude Dev API is not available")
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** To ensure that the `saoudrizwan.claude-dev` extension is activated before your extension, add it to the `extensionDependencies` in your `package.json`:
|
||||
|
||||
```json
|
||||
"extensionDependencies": [
|
||||
"saoudrizwan.claude-dev"
|
||||
]
|
||||
```
|
||||
|
||||
For detailed information on the available methods and their usage, refer to the `claude-dev.d.ts` file.
|
||||
37
src/extension-api/claude-dev.d.ts
vendored
Normal file
37
src/extension-api/claude-dev.d.ts
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
export interface ClaudeDevAPI {
|
||||
/**
|
||||
* Sets the custom instructions in the global storage.
|
||||
* @param value The custom instructions to be saved.
|
||||
*/
|
||||
setCustomInstructions(value: string): Promise<void>
|
||||
|
||||
/**
|
||||
* Retrieves the custom instructions from the global storage.
|
||||
* @returns The saved custom instructions, or undefined if not set.
|
||||
*/
|
||||
getCustomInstructions(): Promise<string | undefined>
|
||||
|
||||
/**
|
||||
* Starts a new task with an optional initial message and images.
|
||||
* @param task Optional initial task message.
|
||||
* @param images Optional array of image data URIs (e.g., "data:image/webp;base64,...").
|
||||
*/
|
||||
startNewTask(task?: string, images?: string[]): Promise<void>
|
||||
|
||||
/**
|
||||
* Sends a message to the current task.
|
||||
* @param message Optional message to send.
|
||||
* @param images Optional array of image data URIs (e.g., "data:image/webp;base64,...").
|
||||
*/
|
||||
sendMessage(message?: string, images?: string[]): Promise<void>
|
||||
|
||||
/**
|
||||
* Simulates pressing the primary button in the chat interface.
|
||||
*/
|
||||
pressPrimaryButton(): Promise<void>
|
||||
|
||||
/**
|
||||
* Simulates pressing the secondary button in the chat interface.
|
||||
*/
|
||||
pressSecondaryButton(): Promise<void>
|
||||
}
|
||||
65
src/extension-api/index.ts
Normal file
65
src/extension-api/index.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import * as vscode from "vscode"
|
||||
import { ClaudeDevProvider } from "../providers/ClaudeDevProvider"
|
||||
import { ClaudeDevAPI } from "./claude-dev"
|
||||
|
||||
export function createClaudeDevAPI(
|
||||
outputChannel: vscode.OutputChannel,
|
||||
sidebarProvider: ClaudeDevProvider
|
||||
): ClaudeDevAPI {
|
||||
const api: ClaudeDevAPI = {
|
||||
setCustomInstructions: async (value: string) => {
|
||||
await sidebarProvider.updateCustomInstructions(value)
|
||||
outputChannel.appendLine("Custom instructions set")
|
||||
},
|
||||
|
||||
getCustomInstructions: async () => {
|
||||
return (await sidebarProvider.getGlobalState("customInstructions")) as string | undefined
|
||||
},
|
||||
|
||||
startNewTask: async (task?: string, images?: string[]) => {
|
||||
outputChannel.appendLine("Starting new task")
|
||||
await sidebarProvider.clearTask()
|
||||
await sidebarProvider.postStateToWebview()
|
||||
await sidebarProvider.postMessageToWebview({ type: "action", action: "chatButtonTapped" })
|
||||
await sidebarProvider.postMessageToWebview({
|
||||
type: "invoke",
|
||||
invoke: "sendMessage",
|
||||
text: task,
|
||||
images: images,
|
||||
})
|
||||
outputChannel.appendLine(
|
||||
`Task started with message: ${task ? `"${task}"` : "undefined"} and ${images?.length || 0} image(s)`
|
||||
)
|
||||
},
|
||||
|
||||
sendMessage: async (message?: string, images?: string[]) => {
|
||||
outputChannel.appendLine(
|
||||
`Sending message: ${message ? `"${message}"` : "undefined"} with ${images?.length || 0} image(s)`
|
||||
)
|
||||
await sidebarProvider.postMessageToWebview({
|
||||
type: "invoke",
|
||||
invoke: "sendMessage",
|
||||
text: message,
|
||||
images: images,
|
||||
})
|
||||
},
|
||||
|
||||
pressPrimaryButton: async () => {
|
||||
outputChannel.appendLine("Pressing primary button")
|
||||
await sidebarProvider.postMessageToWebview({
|
||||
type: "invoke",
|
||||
invoke: "primaryButtonClick",
|
||||
})
|
||||
},
|
||||
|
||||
pressSecondaryButton: async () => {
|
||||
outputChannel.appendLine("Pressing secondary button")
|
||||
await sidebarProvider.postMessageToWebview({
|
||||
type: "invoke",
|
||||
invoke: "secondaryButtonClick",
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
return api
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
import * as vscode from "vscode"
|
||||
import { ClaudeDevProvider } from "./providers/ClaudeDevProvider"
|
||||
import delay from "delay"
|
||||
import { createClaudeDevAPI } from "./extension-api"
|
||||
|
||||
/*
|
||||
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
|
||||
@@ -141,6 +142,8 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
}
|
||||
context.subscriptions.push(vscode.window.registerUriHandler({ handleUri }))
|
||||
|
||||
return createClaudeDevAPI(outputChannel, sidebarProvider)
|
||||
}
|
||||
|
||||
// This method is called when your extension is deactivated
|
||||
|
||||
@@ -373,10 +373,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
|
||||
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()
|
||||
await this.updateCustomInstructions(message.text)
|
||||
break
|
||||
case "alwaysAllowReadOnly":
|
||||
await this.updateGlobalState("alwaysAllowReadOnly", message.bool ?? undefined)
|
||||
@@ -439,6 +436,13 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
|
||||
)
|
||||
}
|
||||
|
||||
async updateCustomInstructions(instructions?: string) {
|
||||
// User may be clearing the field
|
||||
await this.updateGlobalState("customInstructions", instructions || undefined)
|
||||
this.claudeDev?.updateCustomInstructions(instructions || undefined)
|
||||
await this.postStateToWebview()
|
||||
}
|
||||
|
||||
// Ollama
|
||||
|
||||
async getOllamaModels(baseUrl?: string) {
|
||||
@@ -782,11 +786,11 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
|
||||
|
||||
// global
|
||||
|
||||
private async updateGlobalState(key: GlobalStateKey, value: any) {
|
||||
async updateGlobalState(key: GlobalStateKey, value: any) {
|
||||
await this.context.globalState.update(key, value)
|
||||
}
|
||||
|
||||
private async getGlobalState(key: GlobalStateKey) {
|
||||
async getGlobalState(key: GlobalStateKey) {
|
||||
return await this.context.globalState.get(key)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@ import { HistoryItem } from "./HistoryItem"
|
||||
|
||||
// webview will hold state
|
||||
export interface ExtensionMessage {
|
||||
type: "action" | "state" | "selectedImages" | "ollamaModels" | "theme" | "workspaceUpdated"
|
||||
type: "action" | "state" | "selectedImages" | "ollamaModels" | "theme" | "workspaceUpdated" | "invoke"
|
||||
text?: string
|
||||
action?: "chatButtonTapped" | "settingsButtonTapped" | "historyButtonTapped" | "didBecomeVisible"
|
||||
invoke?: "sendMessage" | "primaryButtonClick" | "secondaryButtonClick"
|
||||
state?: ExtensionState
|
||||
images?: string[]
|
||||
models?: string[]
|
||||
|
||||
@@ -181,11 +181,12 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
}
|
||||
}, [messages.length])
|
||||
|
||||
const handleSendMessage = useCallback(() => {
|
||||
const text = inputValue.trim()
|
||||
if (text || selectedImages.length > 0) {
|
||||
const handleSendMessage = useCallback(
|
||||
(text: string, images: string[]) => {
|
||||
text = text.trim()
|
||||
if (text || images.length > 0) {
|
||||
if (messages.length === 0) {
|
||||
vscode.postMessage({ type: "newTask", text, images: selectedImages })
|
||||
vscode.postMessage({ type: "newTask", text, images })
|
||||
} else if (claudeAsk) {
|
||||
switch (claudeAsk) {
|
||||
case "followup":
|
||||
@@ -200,7 +201,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
type: "askResponse",
|
||||
askResponse: "messageResponse",
|
||||
text,
|
||||
images: selectedImages,
|
||||
images,
|
||||
})
|
||||
break
|
||||
// there is no other case that a textfield should be enabled
|
||||
@@ -214,7 +215,9 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
// setPrimaryButtonText(undefined)
|
||||
// setSecondaryButtonText(undefined)
|
||||
}
|
||||
}, [inputValue, selectedImages, messages.length, claudeAsk])
|
||||
},
|
||||
[messages.length, claudeAsk]
|
||||
)
|
||||
|
||||
const startNewTask = useCallback(() => {
|
||||
vscode.postMessage({ type: "clearTask" })
|
||||
@@ -301,6 +304,18 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
)
|
||||
}
|
||||
break
|
||||
case "invoke":
|
||||
switch (message.invoke!) {
|
||||
case "sendMessage":
|
||||
handleSendMessage(message.text ?? "", message.images ?? [])
|
||||
break
|
||||
case "primaryButtonClick":
|
||||
handlePrimaryButtonClick()
|
||||
break
|
||||
case "secondaryButtonClick":
|
||||
handleSecondaryButtonClick()
|
||||
break
|
||||
}
|
||||
}
|
||||
// textAreaRef.current is not explicitly required here since react gaurantees that ref will be stable across re-renders, and we're not using its value but its reference.
|
||||
},
|
||||
@@ -545,7 +560,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
placeholderText={placeholderText}
|
||||
selectedImages={selectedImages}
|
||||
setSelectedImages={setSelectedImages}
|
||||
onSend={handleSendMessage}
|
||||
onSend={() => handleSendMessage(inputValue, selectedImages)}
|
||||
onSelectImages={selectImages}
|
||||
shouldDisableImages={shouldDisableImages}
|
||||
onHeightChange={() => {
|
||||
|
||||
Reference in New Issue
Block a user