Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/activate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { handleUri } from "./handleUri"
export { registerCommands } from "./registerCommands"
export { registerCodeActions } from "./registerCodeActions"
export { registerTerminalActions } from "./registerTerminalActions"
export { registerPearListener } from "./registerPearListener"
85 changes: 85 additions & 0 deletions src/activate/registerPearListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import * as vscode from "vscode"
import { ClineProvider } from "../core/webview/ClineProvider"
import { assert } from "../utils/util"

export const getPearaiExtension = async () => {
const pearAiExtension = vscode.extensions.getExtension("pearai.pearai")

assert(!!pearAiExtension, "PearAI extension not found")

if (!pearAiExtension.isActive) {
await pearAiExtension.activate()
}

return pearAiExtension
}

export const registerPearListener = async () => {
// Getting the pear ai extension instance
const pearAiExtension = await getPearaiExtension()

// Access the API directly from exports
if (pearAiExtension.exports) {
pearAiExtension.exports.pearAPI.creatorMode.onDidRequestExecutePlan(async (msg: any) => {
console.dir(`onDidRequestNewTask triggered with: ${JSON.stringify(msg)}`)

// Get the sidebar provider
const sidebarProvider = ClineProvider.getSidebarInstance()

if (sidebarProvider) {
// Focus the sidebar first
await vscode.commands.executeCommand("pearai-roo-cline.SidebarProvider.focus")

// Wait for the view to be ready using a helper function
await ensureViewIsReady(sidebarProvider)

if (msg.creatorModeConfig?.creatorMode) {
// Switch to creator mode
await sidebarProvider.handleModeSwitch("creator")
await sidebarProvider.postStateToWebview()
}
// Navigate to chat view
await sidebarProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })

// Wait a brief moment for UI to update
await new Promise((resolve) => setTimeout(resolve, 300))

let creatorModeConfig = {
creatorMode: msg.creatorMode,
newProjectType: msg.newProjectType,
newProjectPath: msg.newProjectPath,
}

// Initialize with task
await sidebarProvider.initClineWithTask(msg.plan, undefined, undefined, undefined, creatorModeConfig)
}
})
} else {
console.error("⚠️⚠️ PearAI API not available in exports ⚠️⚠️")
}
}

// TODO: decide if this is needed
// Helper function to ensure the webview is ready
async function ensureViewIsReady(provider: ClineProvider): Promise<void> {
// If the view is already launched, we're good to go
if (provider.viewLaunched) {
return
}

// Otherwise, we need to wait for it to initialize
return new Promise((resolve) => {
// Set up a one-time listener for when the view is ready
const disposable = provider.on("clineCreated", () => {
// Clean up the listener
disposable.dispose()
resolve()
})

// Set a timeout just in case
setTimeout(() => {
disposable.dispose()
resolve()
}, 5000)
})
}
13 changes: 11 additions & 2 deletions src/api/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,18 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
case "claude-3-opus-20240229":
case "claude-3-haiku-20240307":
betas.push("prompt-caching-2024-07-31")
// Include prompt_key if newProjectType is set
return {
headers: { "anthropic-beta": betas.join(",") },
authorization: `Bearer ${this.options.apiKey}`,
headers: {
"anthropic-beta": betas.join(","),
prompt_key: this.options.creatorModeConfig?.newProjectType
? String(this.options.creatorModeConfig.newProjectType)
: undefined,
project_path: this.options.creatorModeConfig?.newProjectPath
? String(this.options.creatorModeConfig.newProjectPath)
: undefined,
authorization: `Bearer ${this.options.apiKey}`,
},
}
default:
return undefined
Expand Down
6 changes: 6 additions & 0 deletions src/core/Cline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import { RooIgnoreController } from "./ignore/RooIgnoreController"
import { type AssistantMessageContent, parseAssistantMessage } from "./assistant-message"
import { truncateConversationIfNeeded } from "./sliding-window"
import { ClineProvider } from "./webview/ClineProvider"
import { creatorModeConfig } from "../shared/pearaiApi"
import { validateToolUse } from "./mode-validator"
import { MultiSearchReplaceDiffStrategy } from "./diff/strategies/multi-search-replace"
import { readApiMessages, saveApiMessages, readTaskMessages, saveTaskMessages, taskMetadata } from "./task-persistence"
Expand Down Expand Up @@ -125,6 +126,7 @@ export type ClineOptions = {
rootTask?: Cline
parentTask?: Cline
taskNumber?: number
creatorModeConfig?: creatorModeConfig
onCreated?: (cline: Cline) => void
pearaiModels?: Record<string, ModelInfo>
}
Expand All @@ -142,6 +144,7 @@ export class Cline extends EventEmitter<ClineEvents> {
pausedModeSlug: string = defaultModeSlug
private pauseInterval: NodeJS.Timeout | undefined

public creatorModeConfig: creatorModeConfig
readonly apiConfiguration: ApiConfiguration
api: ApiHandler
private promptCacheKey: string
Expand Down Expand Up @@ -218,6 +221,7 @@ export class Cline extends EventEmitter<ClineEvents> {
startTask = true,
rootTask,
parentTask,
creatorModeConfig,
taskNumber = -1,
onCreated,
}: ClineOptions) {
Expand Down Expand Up @@ -257,6 +261,8 @@ export class Cline extends EventEmitter<ClineEvents> {
this.diffViewProvider = new DiffViewProvider(this.cwd)
this.enableCheckpoints = enableCheckpoints

this.creatorModeConfig = creatorModeConfig ?? { creatorMode: false }

this.rootTask = rootTask
this.parentTask = parentTask
this.taskNumber = taskNumber
Expand Down
51 changes: 47 additions & 4 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ import { getUri } from "./getUri"
import { getSystemPromptFilePath } from "../prompts/sections/custom-system-prompt"
import { telemetryService } from "../../services/telemetry/TelemetryService"
import { getWorkspacePath } from "../../utils/path"
import { PEARAI_URL, creatorModeConfig } from "../../shared/pearaiApi"
import { webviewMessageHandler } from "./webviewMessageHandler"
import { WebviewMessage } from "../../shared/WebviewMessage"
import { PEARAI_URL } from "../../shared/pearaiApi"
import { PearAIAgentModelsConfig } from "../../api/providers/pearai/pearai"

/**
Expand Down Expand Up @@ -86,6 +86,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
private readonly outputChannel: vscode.OutputChannel,
private readonly renderContext: "sidebar" | "editor" = "sidebar",
public readonly contextProxy: ContextProxy,
private readonly isCreatorView: boolean = false,
) {
super()

Expand Down Expand Up @@ -115,6 +116,16 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
})
}

public static getSidebarInstance(): ClineProvider | undefined {
const sidebar = Array.from(this.activeInstances).find((instance) => !instance.isCreatorView)

if (!sidebar?.view?.visible) {
vscode.commands.executeCommand("pearai-roo-cline.SidebarProvider.focus")
}

return sidebar
}

// Adds a new Cline instance to clineStack, marking the start of a new task.
// The instance is pushed to the top of the stack (LIFO order).
// When the task is completed, the top instance is removed, reactivating the previous task.
Expand Down Expand Up @@ -454,7 +465,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
}

// When initializing a new task, (not from history but from a tool command
// new_task) there is no need to remove the previouse task since the new
// new_task) there is no need to remove the previous task since the new
// task is a subtask of the previous one, and when it finishes it is removed
// from the stack and the caller is resumed in this way we can have a chain
// of tasks, each one being a sub task of the previous one until the main
Expand All @@ -474,6 +485,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
| "experiments"
>
> = {},
creatorModeConfig?: creatorModeConfig,
) {
const {
apiConfiguration,
Expand All @@ -486,14 +498,27 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
experiments,
} = await this.getState()

// Update API configuration with creator mode
await this.updateApiConfiguration({
...apiConfiguration,
creatorModeConfig,
})

// Post updated state to webview immediately
await this.postStateToWebview()

const modePrompt = customModePrompts?.[mode] as PromptComponent
const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")

const pearaiAgentModels = await this.getPearAIAgentModels()

const cline = new Cline({
provider: this,
apiConfiguration: { ...apiConfiguration, pearaiAgentModels: pearaiAgentModels },
apiConfiguration: {
...apiConfiguration,
creatorModeConfig,
pearaiAgentModels,
},
customInstructions: effectiveInstructions,
enableDiff,
enableCheckpoints,
Expand All @@ -504,6 +529,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
rootTask: this.clineStack.length > 0 ? this.clineStack[0] : undefined,
parentTask,
taskNumber: this.clineStack.length + 1,
creatorModeConfig,
onCreated: (cline) => this.emit("clineCreated", cline),
...options,
})
Expand Down Expand Up @@ -824,6 +850,14 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
async updateApiConfiguration(providerSettings: ProviderSettings) {
// Update mode's default config.
const { mode } = await this.getState()
const currentCline = this.getCurrentCline()

// Preserve creator mode when updating configuration
const updatedConfig: ProviderSettings = {
...providerSettings,
creatorModeConfig: currentCline?.creatorModeConfig,
}


if (mode) {
const currentApiConfigName = this.getGlobalState("currentApiConfigName")
Expand All @@ -835,6 +869,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
}
}

await this.updateApiConfiguration(updatedConfig)
await this.contextProxy.setProviderSettings(providerSettings)

if (this.getCurrentCline()) {
Expand Down Expand Up @@ -1150,8 +1185,10 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
}

async getStateToPostToWebview() {
const currentCline = this.getCurrentCline()
// Get base state
const {
apiConfiguration,
apiConfiguration: baseApiConfiguration,
lastShownAnnouncementId,
customInstructions,
alwaysAllowReadOnly,
Expand Down Expand Up @@ -1210,6 +1247,12 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
historyPreviewCollapsed,
} = await this.getState()

// Construct API configuration with creator mode
const apiConfiguration = {
...baseApiConfiguration,
creatorModeConfig: currentCline?.creatorModeConfig,
}

const telemetryKey = process.env.POSTHOG_API_KEY
const machineId = vscode.env.machineId
const allowedCommands = vscode.workspace.getConfiguration("roo-cline").get<string[]>("allowedCommands") || []
Expand Down
7 changes: 7 additions & 0 deletions src/exports/roo-code.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ type ProviderSettings = {
defaultModelId?: string | undefined
}
| undefined
creatorModeConfig?:
| {
creatorMode?: boolean | undefined
newProjectType?: string | undefined
newProjectPath?: string | undefined
}
| undefined
}

type GlobalSettings = {
Expand Down
7 changes: 7 additions & 0 deletions src/exports/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,13 @@ type ProviderSettings = {
defaultModelId?: string | undefined
}
| undefined
creatorModeConfig?:
| {
creatorMode?: boolean | undefined
newProjectType?: string | undefined
newProjectPath?: string | undefined
}
| undefined
}

export type { ProviderSettings }
Expand Down
9 changes: 8 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry"
import { API } from "./exports/api"
import { migrateSettings } from "./utils/migrateSettings"

import { handleUri, registerCommands, registerCodeActions, registerTerminalActions } from "./activate"
import {
handleUri,
registerCommands,
registerCodeActions,
registerTerminalActions,
registerPearListener,
} from "./activate"
import { formatLanguage } from "./shared/language"

/**
Expand Down Expand Up @@ -202,6 +208,7 @@ export async function activate(context: vscode.ExtensionContext) {

registerCodeActions(context)
registerTerminalActions(context)
registerPearListener()

context.subscriptions.push(
vscode.commands.registerCommand("roo-cline.focus", async (...args: any[]) => {
Expand Down
10 changes: 10 additions & 0 deletions src/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,13 @@ export type Experiments = z.infer<typeof experimentsSchema>

type _AssertExperiments = AssertEqual<Equals<ExperimentId, Keys<Experiments>>>


export const creatorModeConfig = z.object({
creatorMode: z.boolean().optional(),
newProjectType: z.string().optional(),
newProjectPath: z.string().optional(),
}).optional()

/**
* ProviderSettings
*/
Expand Down Expand Up @@ -449,6 +456,8 @@ export const providerSettingsSchema = z.object({
defaultModelId: z.string().optional(),
})
.optional(),
creatorModeConfig: creatorModeConfig,

})

export type ProviderSettings = z.infer<typeof providerSettingsSchema>
Expand Down Expand Up @@ -546,6 +555,7 @@ const providerSettingsRecord: ProviderSettingsRecord = {
pearaiApiKey: undefined,
pearaiModelInfo: undefined,
pearaiAgentModels: undefined,
creatorModeConfig: undefined,
// X.AI (Grok)
xaiApiKey: undefined,
}
Expand Down
Empty file added src/shared/creatorMode.ts
Empty file.
7 changes: 7 additions & 0 deletions src/shared/modes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ export function getToolsForMode(groups: readonly GroupEntry[]): string[] {

// Main modes configuration as an ordered array
export const modes: readonly ModeConfig[] = [
{
slug: "creator",
name: "Creator",
roleDefinition:
"You are PearAI Agent (Powered by Roo Code / Cline), a creative and systematic software architect focused on turning high-level ideas into actionable plans. Your primary goal is to help users transform their ideas into structured action plans.",
groups: ["read", ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], "browser", "mcp"],
},
{
slug: "code",
name: "💻 Code",
Expand Down
6 changes: 6 additions & 0 deletions src/shared/pearaiApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,9 @@ export const allModels: { [key: string]: ModelInfo } = {
// Unbound models (single default model)
[`unbound/${unboundDefaultModelId}`]: unboundDefaultModelInfo,
} as const satisfies Record<string, ModelInfo>

export interface creatorModeConfig {
creatorMode?: boolean // Defaults to false when not set
newProjectType?: string
newProjectPath?: string
}
Loading
Loading