mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Clean up types related to tools (#2719)
This commit is contained in:
parent
026091e432
commit
471caff000
38 changed files with 543 additions and 566 deletions
5
.changeset/wise-spies-type.md
Normal file
5
.changeset/wise-spies-type.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"roo-cline": patch
|
||||
---
|
||||
|
||||
Clean up types related to tools
|
||||
|
|
@ -12,20 +12,14 @@ import getFolderSize from "get-folder-size"
|
|||
import { serializeError } from "serialize-error"
|
||||
import * as vscode from "vscode"
|
||||
|
||||
// schemas
|
||||
import { TokenUsage } from "../schemas"
|
||||
|
||||
// api
|
||||
import { ApiHandler, buildApiHandler } from "../api"
|
||||
import { ApiStream } from "../api/transform/stream"
|
||||
import { DIFF_VIEW_URI_SCHEME, DiffViewProvider } from "../integrations/editor/DiffViewProvider"
|
||||
import { CheckpointServiceOptions, RepoPerTaskCheckpointService } from "../services/checkpoints"
|
||||
import { findToolName, formatContentBlockToMarkdown } from "../integrations/misc/export-markdown"
|
||||
import { fetchInstructionsTool } from "./tools/fetchInstructionsTool"
|
||||
import { listFilesTool } from "./tools/listFilesTool"
|
||||
import { readFileTool } from "./tools/readFileTool"
|
||||
import { ExitCodeDetails, TerminalProcess } from "../integrations/terminal/TerminalProcess"
|
||||
import { Terminal } from "../integrations/terminal/Terminal"
|
||||
import { TerminalRegistry } from "../integrations/terminal/TerminalRegistry"
|
||||
import { UrlContentFetcher } from "../services/browser/UrlContentFetcher"
|
||||
import { listFiles } from "../services/glob/list-files"
|
||||
|
||||
// shared
|
||||
import { ApiConfiguration } from "../shared/api"
|
||||
import { findLastIndex } from "../shared/array"
|
||||
import { combineApiRequests } from "../shared/combineApiRequests"
|
||||
|
|
@ -42,26 +36,35 @@ import { getApiMetrics } from "../shared/getApiMetrics"
|
|||
import { HistoryItem } from "../shared/HistoryItem"
|
||||
import { ClineAskResponse } from "../shared/WebviewMessage"
|
||||
import { GlobalFileNames } from "../shared/globalFileNames"
|
||||
import { defaultModeSlug, getModeBySlug, getFullModeDetails } from "../shared/modes"
|
||||
import { defaultModeSlug, getModeBySlug, getFullModeDetails, isToolAllowedForMode } from "../shared/modes"
|
||||
import { EXPERIMENT_IDS, experiments as Experiments, ExperimentId } from "../shared/experiments"
|
||||
import { formatLanguage } from "../shared/language"
|
||||
import { ToolParamName, ToolName, ToolResponse } from "../shared/tools"
|
||||
|
||||
// services
|
||||
import { UrlContentFetcher } from "../services/browser/UrlContentFetcher"
|
||||
import { listFiles } from "../services/glob/list-files"
|
||||
import { BrowserSession } from "../services/browser/BrowserSession"
|
||||
import { McpHub } from "../services/mcp/McpHub"
|
||||
import { telemetryService } from "../services/telemetry/TelemetryService"
|
||||
import { CheckpointServiceOptions, RepoPerTaskCheckpointService } from "../services/checkpoints"
|
||||
|
||||
// integrations
|
||||
import { DIFF_VIEW_URI_SCHEME, DiffViewProvider } from "../integrations/editor/DiffViewProvider"
|
||||
import { findToolName, formatContentBlockToMarkdown } from "../integrations/misc/export-markdown"
|
||||
import { ExitCodeDetails, TerminalProcess } from "../integrations/terminal/TerminalProcess"
|
||||
import { Terminal } from "../integrations/terminal/Terminal"
|
||||
import { TerminalRegistry } from "../integrations/terminal/TerminalRegistry"
|
||||
|
||||
// utils
|
||||
import { calculateApiCostAnthropic } from "../utils/cost"
|
||||
import { fileExistsAtPath } from "../utils/fs"
|
||||
import { arePathsEqual } from "../utils/path"
|
||||
import { parseMentions } from "./mentions"
|
||||
import { FileContextTracker } from "./context-tracking/FileContextTracker"
|
||||
import { RooIgnoreController } from "./ignore/RooIgnoreController"
|
||||
import { AssistantMessageContent, parseAssistantMessage, ToolParamName, ToolUseName } from "./assistant-message"
|
||||
import { formatResponse } from "./prompts/responses"
|
||||
import { SYSTEM_PROMPT } from "./prompts/system"
|
||||
import { truncateConversationIfNeeded } from "./sliding-window"
|
||||
import { ClineProvider } from "./webview/ClineProvider"
|
||||
import { BrowserSession } from "../services/browser/BrowserSession"
|
||||
import { formatLanguage } from "../shared/language"
|
||||
import { McpHub } from "../services/mcp/McpHub"
|
||||
import { DiffStrategy, getDiffStrategy } from "./diff/DiffStrategy"
|
||||
import { telemetryService } from "../services/telemetry/TelemetryService"
|
||||
import { validateToolUse, isToolAllowedForMode, ToolName } from "./mode-validator"
|
||||
import { getWorkspacePath } from "../utils/path"
|
||||
import { arePathsEqual, getWorkspacePath } from "../utils/path"
|
||||
|
||||
// tools
|
||||
import { fetchInstructionsTool } from "./tools/fetchInstructionsTool"
|
||||
import { listFilesTool } from "./tools/listFilesTool"
|
||||
import { readFileTool } from "./tools/readFileTool"
|
||||
import { writeToFileTool } from "./tools/writeToFileTool"
|
||||
import { applyDiffTool } from "./tools/applyDiffTool"
|
||||
import { insertContentTool } from "./tools/insertContentTool"
|
||||
|
|
@ -78,7 +81,20 @@ import { attemptCompletionTool } from "./tools/attemptCompletionTool"
|
|||
import { newTaskTool } from "./tools/newTaskTool"
|
||||
import { appendToFileTool } from "./tools/appendToFileTool"
|
||||
|
||||
export type ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>
|
||||
// prompts
|
||||
import { formatResponse } from "./prompts/responses"
|
||||
import { SYSTEM_PROMPT } from "./prompts/system"
|
||||
|
||||
// ... everything else
|
||||
import { parseMentions } from "./mentions"
|
||||
import { FileContextTracker } from "./context-tracking/FileContextTracker"
|
||||
import { RooIgnoreController } from "./ignore/RooIgnoreController"
|
||||
import { type AssistantMessageContent, parseAssistantMessage } from "./assistant-message"
|
||||
import { truncateConversationIfNeeded } from "./sliding-window"
|
||||
import { ClineProvider } from "./webview/ClineProvider"
|
||||
import { DiffStrategy, getDiffStrategy } from "./diff/DiffStrategy"
|
||||
import { validateToolUse } from "./mode-validator"
|
||||
|
||||
type UserContent = Array<Anthropic.Messages.ContentBlockParam>
|
||||
|
||||
export type ClineEvents = {
|
||||
|
|
@ -573,7 +589,7 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
}
|
||||
}
|
||||
|
||||
async sayAndCreateMissingParamError(toolName: ToolUseName, paramName: string, relPath?: string) {
|
||||
async sayAndCreateMissingParamError(toolName: ToolName, paramName: string, relPath?: string) {
|
||||
await this.say(
|
||||
"error",
|
||||
`Roo tried to use ${toolName}${
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { isToolAllowedForMode, getModeConfig, modes, ModeConfig } from "../../shared/modes"
|
||||
import { TOOL_GROUPS } from "../../shared/tool-groups"
|
||||
import { TOOL_GROUPS } from "../../shared/tools"
|
||||
import { validateToolUse } from "../mode-validator"
|
||||
|
||||
const [codeMode, architectMode, askMode] = modes.map((mode) => mode.slug)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import * as path from "path"
|
||||
|
||||
import { countFileLines } from "../../integrations/misc/line-counter"
|
||||
import { readLines } from "../../integrations/misc/read-lines"
|
||||
import { extractTextFromFile, addLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
|
||||
import { isBinaryFile } from "isbinaryfile"
|
||||
import { ReadFileToolUse } from "../assistant-message"
|
||||
import { Cline } from "../Cline"
|
||||
import { ReadFileToolUse } from "../../shared/tools"
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock("../../integrations/misc/line-counter")
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import * as path from "path"
|
||||
|
||||
import { countFileLines } from "../../integrations/misc/line-counter"
|
||||
import { readLines } from "../../integrations/misc/read-lines"
|
||||
import { extractTextFromFile, addLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import { extractTextFromFile } from "../../integrations/misc/extract-text"
|
||||
import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
|
||||
import { isBinaryFile } from "isbinaryfile"
|
||||
import { ReadFileToolUse } from "../assistant-message"
|
||||
import { Cline } from "../Cline"
|
||||
import { ReadFileToolUse } from "../../shared/tools"
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock("../../integrations/misc/line-counter")
|
||||
|
|
|
|||
|
|
@ -1,156 +1 @@
|
|||
export type AssistantMessageContent = TextContent | ToolUse
|
||||
|
||||
export { parseAssistantMessage } from "./parse-assistant-message"
|
||||
|
||||
export interface TextContent {
|
||||
type: "text"
|
||||
content: string
|
||||
partial: boolean
|
||||
}
|
||||
|
||||
export const toolUseNames = [
|
||||
"execute_command",
|
||||
"read_file",
|
||||
"write_to_file",
|
||||
"append_to_file",
|
||||
"apply_diff",
|
||||
"insert_content",
|
||||
"search_and_replace",
|
||||
"search_files",
|
||||
"list_files",
|
||||
"list_code_definition_names",
|
||||
"browser_action",
|
||||
"use_mcp_tool",
|
||||
"access_mcp_resource",
|
||||
"ask_followup_question",
|
||||
"attempt_completion",
|
||||
"switch_mode",
|
||||
"new_task",
|
||||
"fetch_instructions",
|
||||
] as const
|
||||
|
||||
// Converts array of tool call names into a union type ("execute_command" | "read_file" | ...)
|
||||
export type ToolUseName = (typeof toolUseNames)[number]
|
||||
|
||||
export const toolParamNames = [
|
||||
"command",
|
||||
"path",
|
||||
"content",
|
||||
"line_count",
|
||||
"regex",
|
||||
"file_pattern",
|
||||
"recursive",
|
||||
"action",
|
||||
"url",
|
||||
"coordinate",
|
||||
"text",
|
||||
"server_name",
|
||||
"tool_name",
|
||||
"arguments",
|
||||
"uri",
|
||||
"question",
|
||||
"result",
|
||||
"diff",
|
||||
"start_line",
|
||||
"end_line",
|
||||
"mode_slug",
|
||||
"reason",
|
||||
"operations",
|
||||
"mode",
|
||||
"message",
|
||||
"cwd",
|
||||
"follow_up",
|
||||
"task",
|
||||
"size",
|
||||
] as const
|
||||
|
||||
export type ToolParamName = (typeof toolParamNames)[number]
|
||||
|
||||
export interface ToolUse {
|
||||
type: "tool_use"
|
||||
name: ToolUseName
|
||||
// params is a partial record, allowing only some or none of the possible parameters to be used
|
||||
params: Partial<Record<ToolParamName, string>>
|
||||
partial: boolean
|
||||
}
|
||||
|
||||
export interface ExecuteCommandToolUse extends ToolUse {
|
||||
name: "execute_command"
|
||||
// Pick<Record<ToolParamName, string>, "command"> makes "command" required, but Partial<> makes it optional
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "command" | "cwd">>
|
||||
}
|
||||
|
||||
export interface ReadFileToolUse extends ToolUse {
|
||||
name: "read_file"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "start_line" | "end_line">>
|
||||
}
|
||||
|
||||
export interface FetchInstructionsToolUse extends ToolUse {
|
||||
name: "fetch_instructions"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "task">>
|
||||
}
|
||||
|
||||
export interface WriteToFileToolUse extends ToolUse {
|
||||
name: "write_to_file"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "content" | "line_count">>
|
||||
}
|
||||
|
||||
export interface AppendToFileToolUse extends ToolUse {
|
||||
name: "append_to_file"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "content">>
|
||||
}
|
||||
|
||||
export interface InsertCodeBlockToolUse extends ToolUse {
|
||||
name: "insert_content"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "operations">>
|
||||
}
|
||||
|
||||
export interface SearchFilesToolUse extends ToolUse {
|
||||
name: "search_files"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "regex" | "file_pattern">>
|
||||
}
|
||||
|
||||
export interface ListFilesToolUse extends ToolUse {
|
||||
name: "list_files"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "recursive">>
|
||||
}
|
||||
|
||||
export interface ListCodeDefinitionNamesToolUse extends ToolUse {
|
||||
name: "list_code_definition_names"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path">>
|
||||
}
|
||||
|
||||
export interface BrowserActionToolUse extends ToolUse {
|
||||
name: "browser_action"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "action" | "url" | "coordinate" | "text" | "size">>
|
||||
}
|
||||
|
||||
export interface UseMcpToolToolUse extends ToolUse {
|
||||
name: "use_mcp_tool"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "server_name" | "tool_name" | "arguments">>
|
||||
}
|
||||
|
||||
export interface AccessMcpResourceToolUse extends ToolUse {
|
||||
name: "access_mcp_resource"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "server_name" | "uri">>
|
||||
}
|
||||
|
||||
export interface AskFollowupQuestionToolUse extends ToolUse {
|
||||
name: "ask_followup_question"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "question" | "follow_up">>
|
||||
}
|
||||
|
||||
export interface AttemptCompletionToolUse extends ToolUse {
|
||||
name: "attempt_completion"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "result" | "command">>
|
||||
}
|
||||
|
||||
export interface SwitchModeToolUse extends ToolUse {
|
||||
name: "switch_mode"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "mode_slug" | "reason">>
|
||||
}
|
||||
|
||||
export interface NewTaskToolUse extends ToolUse {
|
||||
name: "new_task"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "mode" | "message">>
|
||||
}
|
||||
export { type AssistantMessageContent, parseAssistantMessage } from "./parse-assistant-message"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
import {
|
||||
AssistantMessageContent,
|
||||
TextContent,
|
||||
ToolUse,
|
||||
ToolParamName,
|
||||
toolParamNames,
|
||||
toolUseNames,
|
||||
ToolUseName,
|
||||
} from "."
|
||||
import { TextContent, ToolUse, ToolParamName, toolParamNames, toolNames, ToolName } from "../../shared/tools"
|
||||
|
||||
export type AssistantMessageContent = TextContent | ToolUse
|
||||
|
||||
export function parseAssistantMessage(assistantMessage: string) {
|
||||
let contentBlocks: AssistantMessageContent[] = []
|
||||
|
|
@ -84,13 +78,13 @@ export function parseAssistantMessage(assistantMessage: string) {
|
|||
// no currentToolUse
|
||||
|
||||
let didStartToolUse = false
|
||||
const possibleToolUseOpeningTags = toolUseNames.map((name) => `<${name}>`)
|
||||
const possibleToolUseOpeningTags = toolNames.map((name) => `<${name}>`)
|
||||
for (const toolUseOpeningTag of possibleToolUseOpeningTags) {
|
||||
if (accumulator.endsWith(toolUseOpeningTag)) {
|
||||
// start of a new tool use
|
||||
currentToolUse = {
|
||||
type: "tool_use",
|
||||
name: toolUseOpeningTag.slice(1, -1) as ToolUseName,
|
||||
name: toolUseOpeningTag.slice(1, -1) as ToolName,
|
||||
params: {},
|
||||
partial: true,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { distance } from "fastest-levenshtein"
|
||||
|
||||
import { DiffStrategy, DiffResult } from "../types"
|
||||
import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from "../../../integrations/misc/extract-text"
|
||||
import { distance } from "fastest-levenshtein"
|
||||
import { ToolProgressStatus } from "../../../shared/ExtensionMessage"
|
||||
import { ToolUse } from "../../assistant-message"
|
||||
import { ToolUse } from "../../../shared/tools"
|
||||
import { normalizeString } from "../../../utils/text-normalization"
|
||||
|
||||
const BUFFER_LINES = 40 // Number of extra context lines to show before and after matches
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
* Interface for implementing different diff strategies
|
||||
*/
|
||||
|
||||
import { ToolUse } from "../../shared/tools"
|
||||
import { ToolProgressStatus } from "../../shared/ExtensionMessage"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
|
||||
export type DiffResult =
|
||||
| { success: true; content: string; failParts?: DiffResult[] }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import { ToolName } from "../shared/tools"
|
||||
import { Mode, isToolAllowedForMode, ModeConfig } from "../shared/modes"
|
||||
import { ToolName } from "../shared/tool-groups"
|
||||
|
||||
export { isToolAllowedForMode }
|
||||
export type { ToolName }
|
||||
|
||||
export function validateToolUse(
|
||||
toolName: ToolName,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { getNewTaskDescription } from "./new-task"
|
|||
import { DiffStrategy } from "../../diff/DiffStrategy"
|
||||
import { McpHub } from "../../../services/mcp/McpHub"
|
||||
import { Mode, ModeConfig, getModeConfig, isToolAllowedForMode, getGroupName } from "../../../shared/modes"
|
||||
import { ToolName, TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS } from "../../../shared/tool-groups"
|
||||
import { ToolName, TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS } from "../../../shared/tools"
|
||||
import { ToolArgs } from "./types"
|
||||
|
||||
// Map of tool names to their description functions
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
// npx jest src/core/tools/__tests__/appendToFileTool.test.ts
|
||||
|
||||
import { describe, expect, it, jest, beforeEach } from "@jest/globals"
|
||||
|
||||
import { appendToFileTool } from "../appendToFileTool"
|
||||
import { Cline } from "../../Cline"
|
||||
import { ToolUse } from "../../assistant-message"
|
||||
import { formatResponse } from "../../prompts/responses"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../types"
|
||||
import { ClineSayTool, ClineAsk } from "../../../shared/ExtensionMessage"
|
||||
import { RecordSource } from "../../context-tracking/FileContextTrackerTypes"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../../shared/tools"
|
||||
import { ClineAsk } from "../../../shared/ExtensionMessage"
|
||||
import { FileContextTracker } from "../../context-tracking/FileContextTracker"
|
||||
import { DiffViewProvider } from "../../../integrations/editor/DiffViewProvider"
|
||||
import { RooIgnoreController } from "../../ignore/RooIgnoreController"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
// npx jest src/core/tools/__tests__/executeCommandTool.test.ts
|
||||
|
||||
import { describe, expect, it, jest, beforeEach } from "@jest/globals"
|
||||
|
||||
import { executeCommandTool } from "../executeCommandTool"
|
||||
import { Cline } from "../../Cline"
|
||||
import { ToolUse } from "../../assistant-message"
|
||||
import { formatResponse } from "../../prompts/responses"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../types"
|
||||
import { ClineAsk } from "../../../schemas"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../../shared/tools"
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock("../../Cline")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { ClineAskUseMcpServer } from "../../shared/ExtensionMessage"
|
||||
import { RemoveClosingTag } from "./types"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { AskApproval, HandleError, PushToolResult } from "./types"
|
||||
import { ToolUse, RemoveClosingTag, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
|
||||
import { Cline } from "../Cline"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
import * as vscode from "vscode"
|
||||
import path from "path"
|
||||
import delay from "delay"
|
||||
|
||||
import { Cline } from "../Cline"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
|
||||
import path from "path"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { addLineNumbers, stripLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import { isPathOutsideWorkspace } from "../../utils/pathUtils"
|
||||
import { everyLineHasLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import delay from "delay"
|
||||
import { unescapeHtmlEntities } from "../../utils/text-normalization"
|
||||
|
||||
export async function appendToFileTool(
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { Cline } from "../Cline"
|
||||
import { RemoveClosingTag } from "./types"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { AskApproval, HandleError, PushToolResult } from "./types"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { addLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import { Cline } from "../Cline"
|
||||
import { ToolUse, RemoveClosingTag } from "../../shared/tools"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { AskApproval, HandleError, PushToolResult } from "../../shared/tools"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { addLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
|
||||
import { telemetryService } from "../../services/telemetry/TelemetryService"
|
||||
import { unescapeHtmlEntities } from "../../utils/text-normalization"
|
||||
|
||||
export async function applyDiffTool(
|
||||
cline: Cline,
|
||||
block: ToolUse,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Cline } from "../Cline"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { parseXml } from "../../utils/xml"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import { ToolResponse } from "../Cline"
|
||||
import Anthropic from "@anthropic-ai/sdk"
|
||||
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { Cline } from "../Cline"
|
||||
import {
|
||||
ToolResponse,
|
||||
ToolUse,
|
||||
AskApproval,
|
||||
HandleError,
|
||||
PushToolResult,
|
||||
RemoveClosingTag,
|
||||
ToolDescription,
|
||||
AskFinishSubTaskApproval,
|
||||
} from "./types"
|
||||
} from "../../shared/tools"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { telemetryService } from "../../services/telemetry/TelemetryService"
|
||||
import Anthropic from "@anthropic-ai/sdk"
|
||||
|
||||
export async function attemptCompletionTool(
|
||||
cline: Cline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Cline } from "../Cline"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import {
|
||||
BrowserAction,
|
||||
BrowserActionResult,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Cline } from "../Cline"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { unescapeHtmlEntities } from "../../utils/text-normalization"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { Cline } from "../Cline"
|
||||
import { fetchInstructions } from "../prompts/instructions/instructions"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { AskApproval, HandleError, PushToolResult } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult } from "../../shared/tools"
|
||||
|
||||
export async function fetchInstructionsTool(
|
||||
cline: Cline,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import delay from "delay"
|
||||
import fs from "fs/promises"
|
||||
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import { Cline } from "../Cline"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import path from "path"
|
||||
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { insertGroups } from "../diff/insert-groups"
|
||||
import delay from "delay"
|
||||
import fs from "fs/promises"
|
||||
|
||||
export async function insertContentTool(
|
||||
cline: Cline,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { ToolUse } from "../assistant-message"
|
||||
import { HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { Cline } from "../Cline"
|
||||
import { AskApproval } from "./types"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { Cline } from "../Cline"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import { parseSourceCodeForDefinitionsTopLevel, parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
|
||||
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import * as path from "path"
|
||||
|
||||
import { Cline } from "../Cline"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { ToolParamName, ToolUse } from "../assistant-message"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { listFiles } from "../../services/glob/list-files"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
|
||||
/**
|
||||
* Implements the list_files tool.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { ToolUse } from "../assistant-message"
|
||||
import { HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import delay from "delay"
|
||||
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { Cline } from "../Cline"
|
||||
import { AskApproval } from "./types"
|
||||
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import delay from "delay"
|
||||
|
||||
export async function newTaskTool(
|
||||
cline: Cline,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import path from "path"
|
||||
import { isBinaryFile } from "isbinaryfile"
|
||||
|
||||
import { Cline } from "../Cline"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { t } from "../../i18n"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
|
||||
import { isPathOutsideWorkspace } from "../../utils/pathUtils"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
|
|
@ -12,7 +13,6 @@ import { countFileLines } from "../../integrations/misc/line-counter"
|
|||
import { readLines } from "../../integrations/misc/read-lines"
|
||||
import { extractTextFromFile, addLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
|
||||
import { isBinaryFile } from "isbinaryfile"
|
||||
|
||||
export async function readFileTool(
|
||||
cline: Cline,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
|
||||
import { Cline } from "../Cline"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import path from "path"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { addLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import fs from "fs/promises"
|
||||
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
|
||||
|
||||
export async function searchAndReplaceTool(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import path from "path"
|
||||
|
||||
import { Cline } from "../Cline"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import path from "path"
|
||||
import { regexSearchFiles } from "../../services/ripgrep"
|
||||
|
||||
export async function searchFilesTool(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Cline } from "../Cline"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { defaultModeSlug } from "../../shared/modes"
|
||||
import { getModeBySlug } from "../../shared/modes"
|
||||
import delay from "delay"
|
||||
|
||||
import { Cline } from "../Cline"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
|
||||
|
||||
export async function switchModeTool(
|
||||
cline: Cline,
|
||||
block: ToolUse,
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
import { ClineAsk, ToolProgressStatus } from "../../schemas"
|
||||
import { ToolParamName } from "../assistant-message"
|
||||
import { ToolResponse } from "../Cline"
|
||||
|
||||
export type AskApproval = (
|
||||
type: ClineAsk,
|
||||
partialMessage?: string,
|
||||
progressStatus?: ToolProgressStatus,
|
||||
) => Promise<boolean>
|
||||
|
||||
export type HandleError = (action: string, error: Error) => Promise<void>
|
||||
|
||||
export type PushToolResult = (content: ToolResponse) => void
|
||||
|
||||
export type RemoveClosingTag = (tag: ToolParamName, content?: string) => string
|
||||
|
||||
export type AskFinishSubTaskApproval = () => Promise<boolean>
|
||||
|
||||
export type ToolDescription = () => string
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import { Cline } from "../Cline"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { ClineAskUseMcpServer } from "../../shared/ExtensionMessage"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
import path from "path"
|
||||
import delay from "delay"
|
||||
import * as vscode from "vscode"
|
||||
|
||||
import { Cline } from "../Cline"
|
||||
import { ClineSayTool } from "../../shared/ExtensionMessage"
|
||||
import { ToolUse } from "../assistant-message"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "./types"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
|
||||
import path from "path"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { addLineNumbers, stripLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import { addLineNumbers, stripLineNumbers, everyLineHasLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import { getReadablePath } from "../../utils/path"
|
||||
import { isPathOutsideWorkspace } from "../../utils/pathUtils"
|
||||
import { everyLineHasLineNumbers } from "../../integrations/misc/extract-text"
|
||||
import delay from "delay"
|
||||
import { detectCodeOmission } from "../../integrations/editor/detect-omission"
|
||||
import { unescapeHtmlEntities } from "../../utils/text-normalization"
|
||||
|
||||
|
|
|
|||
147
src/services/telemetry/PostHogClient.ts
Normal file
147
src/services/telemetry/PostHogClient.ts
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
import { PostHog } from "posthog-node"
|
||||
import * as vscode from "vscode"
|
||||
|
||||
import { logger } from "../../utils/logging"
|
||||
|
||||
// This forward declaration is needed to avoid circular dependencies
|
||||
export interface ClineProviderInterface {
|
||||
// Gets telemetry properties to attach to every event
|
||||
getTelemetryProperties(): Promise<Record<string, any>>
|
||||
}
|
||||
|
||||
/**
|
||||
* PostHogClient handles telemetry event tracking for the Roo Code extension
|
||||
* Uses PostHog analytics to track user interactions and system events
|
||||
* Respects user privacy settings and VSCode's global telemetry configuration
|
||||
*/
|
||||
export class PostHogClient {
|
||||
public static readonly EVENTS = {
|
||||
TASK: {
|
||||
CREATED: "Task Created",
|
||||
RESTARTED: "Task Reopened",
|
||||
COMPLETED: "Task Completed",
|
||||
CONVERSATION_MESSAGE: "Conversation Message",
|
||||
MODE_SWITCH: "Mode Switched",
|
||||
TOOL_USED: "Tool Used",
|
||||
CHECKPOINT_CREATED: "Checkpoint Created",
|
||||
CHECKPOINT_RESTORED: "Checkpoint Restored",
|
||||
CHECKPOINT_DIFFED: "Checkpoint Diffed",
|
||||
CODE_ACTION_USED: "Code Action Used",
|
||||
PROMPT_ENHANCED: "Prompt Enhanced",
|
||||
},
|
||||
ERRORS: {
|
||||
SCHEMA_VALIDATION_ERROR: "Schema Validation Error",
|
||||
DIFF_APPLICATION_ERROR: "Diff Application Error",
|
||||
CONSECUTIVE_MISTAKE_ERROR: "Consecutive Mistake Error",
|
||||
},
|
||||
}
|
||||
|
||||
private static instance: PostHogClient
|
||||
private client: PostHog
|
||||
private distinctId: string = vscode.env.machineId
|
||||
private telemetryEnabled: boolean = false
|
||||
private providerRef: WeakRef<ClineProviderInterface> | null = null
|
||||
|
||||
private constructor() {
|
||||
this.client = new PostHog(process.env.POSTHOG_API_KEY || "", { host: "https://us.i.posthog.com" })
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the telemetry state based on user preferences and VSCode settings
|
||||
* Only enables telemetry if both VSCode global telemetry is enabled and user has opted in
|
||||
* @param didUserOptIn Whether the user has explicitly opted into telemetry
|
||||
*/
|
||||
public updateTelemetryState(didUserOptIn: boolean): void {
|
||||
this.telemetryEnabled = false
|
||||
|
||||
// First check global telemetry level - telemetry should only be enabled when level is "all"
|
||||
const telemetryLevel = vscode.workspace.getConfiguration("telemetry").get<string>("telemetryLevel", "all")
|
||||
const globalTelemetryEnabled = telemetryLevel === "all"
|
||||
|
||||
// We only enable telemetry if global vscode telemetry is enabled
|
||||
if (globalTelemetryEnabled) {
|
||||
this.telemetryEnabled = didUserOptIn
|
||||
}
|
||||
|
||||
// Update PostHog client state based on telemetry preference
|
||||
if (this.telemetryEnabled) {
|
||||
this.client.optIn()
|
||||
} else {
|
||||
this.client.optOut()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or creates the singleton instance of PostHogClient
|
||||
* @returns The PostHogClient instance
|
||||
*/
|
||||
public static getInstance(): PostHogClient {
|
||||
if (!PostHogClient.instance) {
|
||||
PostHogClient.instance = new PostHogClient()
|
||||
}
|
||||
|
||||
return PostHogClient.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ClineProvider reference to use for global properties
|
||||
* @param provider A ClineProvider instance to use
|
||||
*/
|
||||
public setProvider(provider: ClineProviderInterface): void {
|
||||
this.providerRef = new WeakRef(provider)
|
||||
logger.debug("PostHogClient: ClineProvider reference set")
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a telemetry event if telemetry is enabled
|
||||
* @param event The event to capture with its properties
|
||||
*/
|
||||
public async capture(event: { event: string; properties?: any }): Promise<void> {
|
||||
// Only send events if telemetry is enabled
|
||||
if (this.telemetryEnabled) {
|
||||
// Get global properties from ClineProvider if available
|
||||
let globalProperties: Record<string, any> = {}
|
||||
const provider = this.providerRef?.deref()
|
||||
|
||||
if (provider) {
|
||||
try {
|
||||
// Get the telemetry properties directly from the provider
|
||||
globalProperties = await provider.getTelemetryProperties()
|
||||
} catch (error) {
|
||||
// Log error but continue with capturing the event
|
||||
logger.error(
|
||||
`Error getting telemetry properties: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge global properties with event-specific properties
|
||||
// Event properties take precedence in case of conflicts
|
||||
const mergedProperties = {
|
||||
...globalProperties,
|
||||
...(event.properties || {}),
|
||||
}
|
||||
|
||||
this.client.capture({
|
||||
distinctId: this.distinctId,
|
||||
event: event.event,
|
||||
properties: mergedProperties,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if telemetry is currently enabled
|
||||
* @returns Whether telemetry is enabled
|
||||
*/
|
||||
public isTelemetryEnabled(): boolean {
|
||||
return this.telemetryEnabled
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the PostHog client
|
||||
*/
|
||||
public async shutdown(): Promise<void> {
|
||||
await this.client.shutdown()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,152 +1,7 @@
|
|||
import { PostHog } from "posthog-node"
|
||||
import * as vscode from "vscode"
|
||||
import { ZodError } from "zod"
|
||||
|
||||
import { logger } from "../../utils/logging"
|
||||
|
||||
// This forward declaration is needed to avoid circular dependencies
|
||||
interface ClineProviderInterface {
|
||||
// Gets telemetry properties to attach to every event
|
||||
getTelemetryProperties(): Promise<Record<string, any>>
|
||||
}
|
||||
|
||||
/**
|
||||
* PostHogClient handles telemetry event tracking for the Roo Code extension
|
||||
* Uses PostHog analytics to track user interactions and system events
|
||||
* Respects user privacy settings and VSCode's global telemetry configuration
|
||||
*/
|
||||
class PostHogClient {
|
||||
public static readonly EVENTS = {
|
||||
TASK: {
|
||||
CREATED: "Task Created",
|
||||
RESTARTED: "Task Reopened",
|
||||
COMPLETED: "Task Completed",
|
||||
CONVERSATION_MESSAGE: "Conversation Message",
|
||||
MODE_SWITCH: "Mode Switched",
|
||||
TOOL_USED: "Tool Used",
|
||||
CHECKPOINT_CREATED: "Checkpoint Created",
|
||||
CHECKPOINT_RESTORED: "Checkpoint Restored",
|
||||
CHECKPOINT_DIFFED: "Checkpoint Diffed",
|
||||
CODE_ACTION_USED: "Code Action Used",
|
||||
PROMPT_ENHANCED: "Prompt Enhanced",
|
||||
},
|
||||
ERRORS: {
|
||||
SCHEMA_VALIDATION_ERROR: "Schema Validation Error",
|
||||
DIFF_APPLICATION_ERROR: "Diff Application Error",
|
||||
CONSECUTIVE_MISTAKE_ERROR: "Consecutive Mistake Error",
|
||||
},
|
||||
}
|
||||
|
||||
private static instance: PostHogClient
|
||||
private client: PostHog
|
||||
private distinctId: string = vscode.env.machineId
|
||||
private telemetryEnabled: boolean = false
|
||||
private providerRef: WeakRef<ClineProviderInterface> | null = null
|
||||
|
||||
private constructor() {
|
||||
this.client = new PostHog(process.env.POSTHOG_API_KEY || "", {
|
||||
host: "https://us.i.posthog.com",
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the telemetry state based on user preferences and VSCode settings
|
||||
* Only enables telemetry if both VSCode global telemetry is enabled and user has opted in
|
||||
* @param didUserOptIn Whether the user has explicitly opted into telemetry
|
||||
*/
|
||||
public updateTelemetryState(didUserOptIn: boolean): void {
|
||||
this.telemetryEnabled = false
|
||||
|
||||
// First check global telemetry level - telemetry should only be enabled when level is "all"
|
||||
const telemetryLevel = vscode.workspace.getConfiguration("telemetry").get<string>("telemetryLevel", "all")
|
||||
const globalTelemetryEnabled = telemetryLevel === "all"
|
||||
|
||||
// We only enable telemetry if global vscode telemetry is enabled
|
||||
if (globalTelemetryEnabled) {
|
||||
this.telemetryEnabled = didUserOptIn
|
||||
}
|
||||
|
||||
// Update PostHog client state based on telemetry preference
|
||||
if (this.telemetryEnabled) {
|
||||
this.client.optIn()
|
||||
} else {
|
||||
this.client.optOut()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or creates the singleton instance of PostHogClient
|
||||
* @returns The PostHogClient instance
|
||||
*/
|
||||
public static getInstance(): PostHogClient {
|
||||
if (!PostHogClient.instance) {
|
||||
PostHogClient.instance = new PostHogClient()
|
||||
}
|
||||
return PostHogClient.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ClineProvider reference to use for global properties
|
||||
* @param provider A ClineProvider instance to use
|
||||
*/
|
||||
public setProvider(provider: ClineProviderInterface): void {
|
||||
this.providerRef = new WeakRef(provider)
|
||||
logger.debug("PostHogClient: ClineProvider reference set")
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a telemetry event if telemetry is enabled
|
||||
* @param event The event to capture with its properties
|
||||
*/
|
||||
public async capture(event: { event: string; properties?: any }): Promise<void> {
|
||||
// Only send events if telemetry is enabled
|
||||
if (this.telemetryEnabled) {
|
||||
// Get global properties from ClineProvider if available
|
||||
let globalProperties: Record<string, any> = {}
|
||||
const provider = this.providerRef?.deref()
|
||||
|
||||
if (provider) {
|
||||
try {
|
||||
// Get the telemetry properties directly from the provider
|
||||
globalProperties = await provider.getTelemetryProperties()
|
||||
} catch (error) {
|
||||
// Log error but continue with capturing the event
|
||||
logger.error(
|
||||
`Error getting telemetry properties: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge global properties with event-specific properties
|
||||
// Event properties take precedence in case of conflicts
|
||||
const mergedProperties = {
|
||||
...globalProperties,
|
||||
...(event.properties || {}),
|
||||
}
|
||||
|
||||
this.client.capture({
|
||||
distinctId: this.distinctId,
|
||||
event: event.event,
|
||||
properties: mergedProperties,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if telemetry is currently enabled
|
||||
* @returns Whether telemetry is enabled
|
||||
*/
|
||||
public isTelemetryEnabled(): boolean {
|
||||
return this.telemetryEnabled
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the PostHog client
|
||||
*/
|
||||
public async shutdown(): Promise<void> {
|
||||
await this.client.shutdown()
|
||||
}
|
||||
}
|
||||
import { PostHogClient, ClineProviderInterface } from "./PostHogClient"
|
||||
|
||||
/**
|
||||
* TelemetryService wrapper class that defers PostHogClient initialization
|
||||
|
|
@ -155,7 +10,6 @@ class PostHogClient {
|
|||
class TelemetryService {
|
||||
private client: PostHogClient | null = null
|
||||
private initialized = false
|
||||
private providerRef: WeakRef<ClineProviderInterface> | null = null
|
||||
|
||||
/**
|
||||
* Initialize the telemetry service with the PostHog client
|
||||
|
|
@ -179,12 +33,11 @@ class TelemetryService {
|
|||
* @param provider A ClineProvider instance to use
|
||||
*/
|
||||
public setProvider(provider: ClineProviderInterface): void {
|
||||
// Keep a weak reference to avoid memory leaks
|
||||
this.providerRef = new WeakRef(provider)
|
||||
// If client is initialized, pass the provider reference
|
||||
if (this.isReady()) {
|
||||
if (this.isReady) {
|
||||
this.client!.setProvider(provider)
|
||||
}
|
||||
|
||||
logger.debug("TelemetryService: ClineProvider reference set")
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +46,7 @@ class TelemetryService {
|
|||
* Checks if the service is initialized before performing any operation
|
||||
* @returns Whether the service is ready to use
|
||||
*/
|
||||
private isReady(): boolean {
|
||||
private get isReady(): boolean {
|
||||
return this.initialized && this.client !== null
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +55,10 @@ class TelemetryService {
|
|||
* @param didUserOptIn Whether the user has explicitly opted into telemetry
|
||||
*/
|
||||
public updateTelemetryState(didUserOptIn: boolean): void {
|
||||
if (!this.isReady()) return
|
||||
if (!this.isReady) {
|
||||
return
|
||||
}
|
||||
|
||||
this.client!.updateTelemetryState(didUserOptIn)
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +67,10 @@ class TelemetryService {
|
|||
* @param event The event to capture with its properties
|
||||
*/
|
||||
public capture(event: { event: string; properties?: any }): void {
|
||||
if (!this.isReady()) return
|
||||
if (!this.isReady) {
|
||||
return
|
||||
}
|
||||
|
||||
this.client!.capture(event)
|
||||
}
|
||||
|
||||
|
|
@ -238,24 +97,15 @@ class TelemetryService {
|
|||
}
|
||||
|
||||
public captureConversationMessage(taskId: string, source: "user" | "assistant"): void {
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.CONVERSATION_MESSAGE, {
|
||||
taskId,
|
||||
source,
|
||||
})
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.CONVERSATION_MESSAGE, { taskId, source })
|
||||
}
|
||||
|
||||
public captureModeSwitch(taskId: string, newMode: string): void {
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.MODE_SWITCH, {
|
||||
taskId,
|
||||
newMode,
|
||||
})
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.MODE_SWITCH, { taskId, newMode })
|
||||
}
|
||||
|
||||
public captureToolUsage(taskId: string, tool: string): void {
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.TOOL_USED, {
|
||||
taskId,
|
||||
tool,
|
||||
})
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.TOOL_USED, { taskId, tool })
|
||||
}
|
||||
|
||||
public captureCheckpointCreated(taskId: string): void {
|
||||
|
|
@ -271,36 +121,24 @@ class TelemetryService {
|
|||
}
|
||||
|
||||
public captureCodeActionUsed(actionType: string): void {
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.CODE_ACTION_USED, {
|
||||
actionType,
|
||||
})
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.CODE_ACTION_USED, { actionType })
|
||||
}
|
||||
|
||||
public capturePromptEnhanced(taskId?: string): void {
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.PROMPT_ENHANCED, {
|
||||
...(taskId && { taskId }),
|
||||
})
|
||||
this.captureEvent(PostHogClient.EVENTS.TASK.PROMPT_ENHANCED, { ...(taskId && { taskId }) })
|
||||
}
|
||||
|
||||
public captureSchemaValidationError({ schemaName, error }: { schemaName: string; error: ZodError }): void {
|
||||
this.captureEvent(PostHogClient.EVENTS.ERRORS.SCHEMA_VALIDATION_ERROR, {
|
||||
schemaName,
|
||||
// https://zod.dev/ERROR_HANDLING?id=formatting-errors
|
||||
error: error.format(),
|
||||
})
|
||||
// https://zod.dev/ERROR_HANDLING?id=formatting-errors
|
||||
this.captureEvent(PostHogClient.EVENTS.ERRORS.SCHEMA_VALIDATION_ERROR, { schemaName, error: error.format() })
|
||||
}
|
||||
|
||||
public captureDiffApplicationError(taskId: string, consecutiveMistakeCount: number): void {
|
||||
this.captureEvent(PostHogClient.EVENTS.ERRORS.DIFF_APPLICATION_ERROR, {
|
||||
taskId,
|
||||
consecutiveMistakeCount,
|
||||
})
|
||||
this.captureEvent(PostHogClient.EVENTS.ERRORS.DIFF_APPLICATION_ERROR, { taskId, consecutiveMistakeCount })
|
||||
}
|
||||
|
||||
public captureConsecutiveMistakeError(taskId: string): void {
|
||||
this.captureEvent(PostHogClient.EVENTS.ERRORS.CONSECUTIVE_MISTAKE_ERROR, {
|
||||
taskId,
|
||||
})
|
||||
this.captureEvent(PostHogClient.EVENTS.ERRORS.CONSECUTIVE_MISTAKE_ERROR, { taskId })
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -308,15 +146,17 @@ class TelemetryService {
|
|||
* @returns Whether telemetry is enabled
|
||||
*/
|
||||
public isTelemetryEnabled(): boolean {
|
||||
if (!this.isReady()) return false
|
||||
return this.client!.isTelemetryEnabled()
|
||||
return this.isReady && this.client!.isTelemetryEnabled()
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the PostHog client
|
||||
*/
|
||||
public async shutdown(): Promise<void> {
|
||||
if (!this.isReady()) return
|
||||
if (!this.isReady) {
|
||||
return
|
||||
}
|
||||
|
||||
await this.client!.shutdown()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import * as vscode from "vscode"
|
||||
|
||||
import { GroupOptions, GroupEntry, ModeConfig, PromptComponent, CustomModePrompts } from "../schemas"
|
||||
import { TOOL_GROUPS, ToolGroup, ALWAYS_AVAILABLE_TOOLS } from "./tool-groups"
|
||||
import { TOOL_GROUPS, ToolGroup, ALWAYS_AVAILABLE_TOOLS } from "./tools"
|
||||
import { addCustomInstructions } from "../core/prompts/sections/custom-instructions"
|
||||
|
||||
export type Mode = string
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
import type { ToolGroup } from "../schemas"
|
||||
|
||||
// Define tool group configuration
|
||||
export type ToolGroupConfig = {
|
||||
tools: readonly string[]
|
||||
alwaysAvailable?: boolean // Whether this group is always available and shouldn't show in prompts view
|
||||
}
|
||||
|
||||
// Map of tool slugs to their display names
|
||||
export const TOOL_DISPLAY_NAMES = {
|
||||
execute_command: "run commands",
|
||||
read_file: "read files",
|
||||
fetch_instructions: "fetch instructions",
|
||||
write_to_file: "write files",
|
||||
append_to_file: "append to files",
|
||||
apply_diff: "apply changes",
|
||||
search_files: "search files",
|
||||
list_files: "list files",
|
||||
list_code_definition_names: "list definitions",
|
||||
browser_action: "use a browser",
|
||||
use_mcp_tool: "use mcp tools",
|
||||
access_mcp_resource: "access mcp resources",
|
||||
ask_followup_question: "ask questions",
|
||||
attempt_completion: "complete tasks",
|
||||
switch_mode: "switch modes",
|
||||
new_task: "create new task",
|
||||
} as const
|
||||
|
||||
export type { ToolGroup }
|
||||
|
||||
// Define available tool groups
|
||||
export const TOOL_GROUPS: Record<ToolGroup, ToolGroupConfig> = {
|
||||
read: {
|
||||
tools: ["read_file", "fetch_instructions", "search_files", "list_files", "list_code_definition_names"],
|
||||
},
|
||||
edit: {
|
||||
tools: ["apply_diff", "write_to_file", "append_to_file", "insert_content", "search_and_replace"],
|
||||
},
|
||||
browser: {
|
||||
tools: ["browser_action"],
|
||||
},
|
||||
command: {
|
||||
tools: ["execute_command"],
|
||||
},
|
||||
mcp: {
|
||||
tools: ["use_mcp_tool", "access_mcp_resource"],
|
||||
},
|
||||
modes: {
|
||||
tools: ["switch_mode", "new_task"],
|
||||
alwaysAvailable: true,
|
||||
},
|
||||
}
|
||||
|
||||
// Tools that are always available to all modes
|
||||
export const ALWAYS_AVAILABLE_TOOLS = [
|
||||
"ask_followup_question",
|
||||
"attempt_completion",
|
||||
"switch_mode",
|
||||
"new_task",
|
||||
] as const
|
||||
|
||||
// Tool name types for type safety
|
||||
export type ToolName = keyof typeof TOOL_DISPLAY_NAMES
|
||||
|
||||
// Tool helper functions
|
||||
export function getToolName(toolConfig: string | readonly [ToolName, ...any[]]): ToolName {
|
||||
return typeof toolConfig === "string" ? (toolConfig as ToolName) : toolConfig[0]
|
||||
}
|
||||
|
||||
export function getToolOptions(toolConfig: string | readonly [ToolName, ...any[]]): any {
|
||||
return typeof toolConfig === "string" ? undefined : toolConfig[1]
|
||||
}
|
||||
236
src/shared/tools.ts
Normal file
236
src/shared/tools.ts
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
|
||||
import { ClineAsk, ToolProgressStatus, ToolGroup } from "../schemas"
|
||||
|
||||
export type ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>
|
||||
|
||||
export type AskApproval = (
|
||||
type: ClineAsk,
|
||||
partialMessage?: string,
|
||||
progressStatus?: ToolProgressStatus,
|
||||
) => Promise<boolean>
|
||||
|
||||
export type HandleError = (action: string, error: Error) => Promise<void>
|
||||
|
||||
export type PushToolResult = (content: ToolResponse) => void
|
||||
|
||||
export type RemoveClosingTag = (tag: ToolParamName, content?: string) => string
|
||||
|
||||
export type AskFinishSubTaskApproval = () => Promise<boolean>
|
||||
|
||||
export type ToolDescription = () => string
|
||||
|
||||
export interface TextContent {
|
||||
type: "text"
|
||||
content: string
|
||||
partial: boolean
|
||||
}
|
||||
|
||||
export const toolNames = [
|
||||
"execute_command",
|
||||
"read_file",
|
||||
"write_to_file",
|
||||
"append_to_file",
|
||||
"apply_diff",
|
||||
"insert_content",
|
||||
"search_and_replace",
|
||||
"search_files",
|
||||
"list_files",
|
||||
"list_code_definition_names",
|
||||
"browser_action",
|
||||
"use_mcp_tool",
|
||||
"access_mcp_resource",
|
||||
"ask_followup_question",
|
||||
"attempt_completion",
|
||||
"switch_mode",
|
||||
"new_task",
|
||||
"fetch_instructions",
|
||||
] as const
|
||||
|
||||
export type ToolName = (typeof toolNames)[number]
|
||||
|
||||
export const toolParamNames = [
|
||||
"command",
|
||||
"path",
|
||||
"content",
|
||||
"line_count",
|
||||
"regex",
|
||||
"file_pattern",
|
||||
"recursive",
|
||||
"action",
|
||||
"url",
|
||||
"coordinate",
|
||||
"text",
|
||||
"server_name",
|
||||
"tool_name",
|
||||
"arguments",
|
||||
"uri",
|
||||
"question",
|
||||
"result",
|
||||
"diff",
|
||||
"start_line",
|
||||
"end_line",
|
||||
"mode_slug",
|
||||
"reason",
|
||||
"operations",
|
||||
"mode",
|
||||
"message",
|
||||
"cwd",
|
||||
"follow_up",
|
||||
"task",
|
||||
"size",
|
||||
] as const
|
||||
|
||||
export type ToolParamName = (typeof toolParamNames)[number]
|
||||
|
||||
export interface ToolUse {
|
||||
type: "tool_use"
|
||||
name: ToolName
|
||||
// params is a partial record, allowing only some or none of the possible parameters to be used
|
||||
params: Partial<Record<ToolParamName, string>>
|
||||
partial: boolean
|
||||
}
|
||||
|
||||
export interface ExecuteCommandToolUse extends ToolUse {
|
||||
name: "execute_command"
|
||||
// Pick<Record<ToolParamName, string>, "command"> makes "command" required, but Partial<> makes it optional
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "command" | "cwd">>
|
||||
}
|
||||
|
||||
export interface ReadFileToolUse extends ToolUse {
|
||||
name: "read_file"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "start_line" | "end_line">>
|
||||
}
|
||||
|
||||
export interface FetchInstructionsToolUse extends ToolUse {
|
||||
name: "fetch_instructions"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "task">>
|
||||
}
|
||||
|
||||
export interface WriteToFileToolUse extends ToolUse {
|
||||
name: "write_to_file"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "content" | "line_count">>
|
||||
}
|
||||
|
||||
export interface InsertCodeBlockToolUse extends ToolUse {
|
||||
name: "insert_content"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "operations">>
|
||||
}
|
||||
|
||||
export interface SearchFilesToolUse extends ToolUse {
|
||||
name: "search_files"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "regex" | "file_pattern">>
|
||||
}
|
||||
|
||||
export interface ListFilesToolUse extends ToolUse {
|
||||
name: "list_files"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path" | "recursive">>
|
||||
}
|
||||
|
||||
export interface ListCodeDefinitionNamesToolUse extends ToolUse {
|
||||
name: "list_code_definition_names"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "path">>
|
||||
}
|
||||
|
||||
export interface BrowserActionToolUse extends ToolUse {
|
||||
name: "browser_action"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "action" | "url" | "coordinate" | "text" | "size">>
|
||||
}
|
||||
|
||||
export interface UseMcpToolToolUse extends ToolUse {
|
||||
name: "use_mcp_tool"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "server_name" | "tool_name" | "arguments">>
|
||||
}
|
||||
|
||||
export interface AccessMcpResourceToolUse extends ToolUse {
|
||||
name: "access_mcp_resource"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "server_name" | "uri">>
|
||||
}
|
||||
|
||||
export interface AskFollowupQuestionToolUse extends ToolUse {
|
||||
name: "ask_followup_question"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "question" | "follow_up">>
|
||||
}
|
||||
|
||||
export interface AttemptCompletionToolUse extends ToolUse {
|
||||
name: "attempt_completion"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "result" | "command">>
|
||||
}
|
||||
|
||||
export interface SwitchModeToolUse extends ToolUse {
|
||||
name: "switch_mode"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "mode_slug" | "reason">>
|
||||
}
|
||||
|
||||
export interface NewTaskToolUse extends ToolUse {
|
||||
name: "new_task"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "mode" | "message">>
|
||||
}
|
||||
|
||||
export type ToolUsage = Record<
|
||||
ToolName,
|
||||
{
|
||||
attempts: number
|
||||
failures: number
|
||||
}
|
||||
>
|
||||
|
||||
// Define tool group configuration
|
||||
export type ToolGroupConfig = {
|
||||
tools: readonly string[]
|
||||
alwaysAvailable?: boolean // Whether this group is always available and shouldn't show in prompts view
|
||||
}
|
||||
|
||||
export const TOOL_DISPLAY_NAMES: Record<ToolName, string> = {
|
||||
execute_command: "run commands",
|
||||
read_file: "read files",
|
||||
fetch_instructions: "fetch instructions",
|
||||
write_to_file: "write files",
|
||||
append_to_file: "append to files",
|
||||
apply_diff: "apply changes",
|
||||
search_files: "search files",
|
||||
list_files: "list files",
|
||||
list_code_definition_names: "list definitions",
|
||||
browser_action: "use a browser",
|
||||
use_mcp_tool: "use mcp tools",
|
||||
access_mcp_resource: "access mcp resources",
|
||||
ask_followup_question: "ask questions",
|
||||
attempt_completion: "complete tasks",
|
||||
switch_mode: "switch modes",
|
||||
new_task: "create new task",
|
||||
insert_content: "insert content",
|
||||
search_and_replace: "search and replace",
|
||||
} as const
|
||||
|
||||
export type { ToolGroup }
|
||||
|
||||
// Define available tool groups.
|
||||
export const TOOL_GROUPS: Record<ToolGroup, ToolGroupConfig> = {
|
||||
read: {
|
||||
tools: ["read_file", "fetch_instructions", "search_files", "list_files", "list_code_definition_names"],
|
||||
},
|
||||
edit: {
|
||||
tools: ["apply_diff", "write_to_file", "append_to_file", "insert_content", "search_and_replace"],
|
||||
},
|
||||
browser: {
|
||||
tools: ["browser_action"],
|
||||
},
|
||||
command: {
|
||||
tools: ["execute_command"],
|
||||
},
|
||||
mcp: {
|
||||
tools: ["use_mcp_tool", "access_mcp_resource"],
|
||||
},
|
||||
modes: {
|
||||
tools: ["switch_mode", "new_task"],
|
||||
alwaysAvailable: true,
|
||||
},
|
||||
}
|
||||
|
||||
// Tools that are always available to all modes.
|
||||
export const ALWAYS_AVAILABLE_TOOLS: ToolName[] = [
|
||||
"ask_followup_question",
|
||||
"attempt_completion",
|
||||
"switch_mode",
|
||||
"new_task",
|
||||
] as const
|
||||
|
|
@ -9,6 +9,7 @@ import {
|
|||
VSCodeRadioGroup,
|
||||
VSCodeRadio,
|
||||
} from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||
import {
|
||||
Mode,
|
||||
|
|
@ -22,7 +23,7 @@ import {
|
|||
import { modeConfigSchema } from "../../../../src/schemas"
|
||||
import { supportPrompt, SupportPromptType } from "../../../../src/shared/support-prompt"
|
||||
|
||||
import { TOOL_GROUPS, ToolGroup } from "../../../../src/shared/tool-groups"
|
||||
import { TOOL_GROUPS, ToolGroup } from "../../../../src/shared/tools"
|
||||
import { vscode } from "../../utils/vscode"
|
||||
import { Tab, TabContent, TabHeader } from "../common/Tab"
|
||||
import i18next from "i18next"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue