mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
Implement parseMentions
This commit is contained in:
@@ -26,6 +26,7 @@ import { findLast, findLastIndex, formatContentBlockToMarkdown } from "./utils"
|
||||
import { truncateHalfConversation } from "./utils/context-management"
|
||||
import { extractTextFromFile } from "./utils/extract-text"
|
||||
import { regexSearchFiles } from "./utils/ripgrep"
|
||||
import { parseMentions } from "./utils/context-mentions"
|
||||
|
||||
const SYSTEM_PROMPT =
|
||||
async () => `You are Claude Dev, a highly skilled software developer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.
|
||||
@@ -420,6 +421,9 @@ export class ClaudeDev {
|
||||
if (this.lastMessageTs !== askTs) {
|
||||
throw new Error("Current ask promise was ignored") // could happen if we send multiple asks in a row i.e. with command_output. It's important that when we know an ask could fail, it is handled gracefully
|
||||
}
|
||||
if (this.askResponse === "messageResponse" && this.askResponseText) {
|
||||
this.askResponseText = await parseMentions(this.askResponseText, cwd, this.providerRef.deref()?.urlScraper)
|
||||
}
|
||||
const result = { response: this.askResponse!, text: this.askResponseText, images: this.askResponseImages }
|
||||
this.askResponse = undefined
|
||||
this.askResponseText = undefined
|
||||
|
||||
@@ -54,7 +54,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
|
||||
private view?: vscode.WebviewView | vscode.WebviewPanel
|
||||
private claudeDev?: ClaudeDev
|
||||
private workspaceTracker?: WorkspaceTracker
|
||||
private urlScraper?: UrlScraper
|
||||
urlScraper?: UrlScraper
|
||||
private latestAnnouncementId = "sep-14-2024" // update to some unique identifier when we add a new announcement
|
||||
|
||||
constructor(readonly context: vscode.ExtensionContext, private readonly outputChannel: vscode.OutputChannel) {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as vscode from "vscode"
|
||||
import * as path from "path"
|
||||
import { openFile } from "./open-file"
|
||||
import { UrlScraper } from "./UrlScraper"
|
||||
import { mentionRegexGlobal } from "../shared/context-mentions"
|
||||
import fs from "fs/promises"
|
||||
import { extractTextFromFile } from "./extract-text"
|
||||
|
||||
export function openMention(mention?: string): void {
|
||||
if (!mention) {
|
||||
@@ -26,3 +30,126 @@ export function openMention(mention?: string): void {
|
||||
vscode.env.openExternal(vscode.Uri.parse(mention))
|
||||
}
|
||||
}
|
||||
|
||||
export async function parseMentions(text: string, cwd: string, urlScraper?: UrlScraper): Promise<string> {
|
||||
const mentions: Set<string> = new Set()
|
||||
let parsedText = text.replace(mentionRegexGlobal, (match, mention) => {
|
||||
mentions.add(mention)
|
||||
if (mention.startsWith("http")) {
|
||||
return `'${mention}' (see below for site content)`
|
||||
} else if (mention.startsWith("/")) {
|
||||
return mention.endsWith("/")
|
||||
? `'${mention}' (see below for folder contents)`
|
||||
: `'${mention}' (see below for file contents)`
|
||||
} else if (mention === "problems") {
|
||||
return `Workspace Problems (see below for diagnostics)`
|
||||
}
|
||||
return match
|
||||
})
|
||||
|
||||
for (const mention of mentions) {
|
||||
if (mention.startsWith("http") && urlScraper) {
|
||||
try {
|
||||
const markdown = await urlScraper.urlToMarkdown(mention)
|
||||
parsedText += `\n\n<url_content url="${mention}">\n${markdown}\n</url_content>`
|
||||
} catch (error) {
|
||||
parsedText += `\n\n<url_content url="${mention}">\nError fetching content: ${error.message}\n</url_content>`
|
||||
}
|
||||
} else if (mention.startsWith("/")) {
|
||||
const mentionPath = mention.slice(1) // Remove the leading '/'
|
||||
try {
|
||||
const content = await getFileOrFolderContent(mentionPath, cwd)
|
||||
if (mention.endsWith("/")) {
|
||||
parsedText += `\n\n<folder_content path="${mentionPath}">\n${content}\n</folder_content>`
|
||||
} else {
|
||||
parsedText += `\n\n<file_content path="${mentionPath}">\n${content}\n</file_content>`
|
||||
}
|
||||
} catch (error) {
|
||||
if (mention.endsWith("/")) {
|
||||
parsedText += `\n\n<folder_content path="${mentionPath}">\nError fetching content: ${error.message}\n</folder_content>`
|
||||
} else {
|
||||
parsedText += `\n\n<file_content path="${mentionPath}">\nError fetching content: ${error.message}\n</file_content>`
|
||||
}
|
||||
}
|
||||
} else if (mention === "problems") {
|
||||
try {
|
||||
const diagnostics = await getWorkspaceDiagnostics(cwd)
|
||||
parsedText += `\n\n<workspace_diagnostics>\n${diagnostics}\n</workspace_diagnostics>`
|
||||
} catch (error) {
|
||||
parsedText += `\n\n<workspace_diagnostics>\nError fetching diagnostics: ${error.message}\n</workspace_diagnostics>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parsedText
|
||||
}
|
||||
|
||||
async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise<string> {
|
||||
const absPath = path.resolve(cwd, mentionPath)
|
||||
|
||||
try {
|
||||
const stats = await fs.stat(absPath)
|
||||
|
||||
if (stats.isFile()) {
|
||||
const content = await extractTextFromFile(absPath)
|
||||
return content
|
||||
} else if (stats.isDirectory()) {
|
||||
const entries = await fs.readdir(absPath, { withFileTypes: true })
|
||||
let directoryContent = ""
|
||||
const fileContentPromises: Promise<string>[] = []
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isFile()) {
|
||||
directoryContent += `- File: ${entry.name}\n`
|
||||
const filePath = path.join(mentionPath, entry.name)
|
||||
const absoluteFilePath = path.resolve(absPath, entry.name)
|
||||
// const relativeFilePath = path.relative(cwd, absoluteFilePath);
|
||||
fileContentPromises.push(
|
||||
extractTextFromFile(absoluteFilePath)
|
||||
.then((content) => `<file_content path="${filePath}">\n${content}\n</file_content>`)
|
||||
.catch(
|
||||
(error) =>
|
||||
`<file_content path="${filePath}">\nError fetching content: ${error.message}\n</file_content>`
|
||||
)
|
||||
)
|
||||
} else if (entry.isDirectory()) {
|
||||
directoryContent += `- Directory: ${entry.name}/\n`
|
||||
// not recursively getting folder contents
|
||||
} else {
|
||||
directoryContent += `- Other: ${entry.name}\n`
|
||||
}
|
||||
})
|
||||
const fileContents = await Promise.all(fileContentPromises)
|
||||
return `${directoryContent}\n${fileContents.join("\n")}`
|
||||
} else {
|
||||
return "Unsupported file type."
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to access path "${mentionPath}": ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function getWorkspaceDiagnostics(cwd: string): Promise<string> {
|
||||
const diagnostics = vscode.languages.getDiagnostics()
|
||||
|
||||
let diagnosticsDetails = ""
|
||||
for (const [uri, fileDiagnostics] of diagnostics) {
|
||||
const problems = fileDiagnostics.filter(
|
||||
(d) => d.severity === vscode.DiagnosticSeverity.Error || d.severity === vscode.DiagnosticSeverity.Warning
|
||||
)
|
||||
if (problems.length > 0) {
|
||||
diagnosticsDetails += `\nFile: ${path.relative(cwd, uri.fsPath)}`
|
||||
for (const diagnostic of problems) {
|
||||
let severity = diagnostic.severity === vscode.DiagnosticSeverity.Error ? "Error" : "Warning"
|
||||
const line = diagnostic.range.start.line + 1 // VSCode lines are 0-indexed
|
||||
const source = diagnostic.source ? `${diagnostic.source} ` : ""
|
||||
diagnosticsDetails += `\n- [${source}${severity}] Line ${line}: ${diagnostic.message}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!diagnosticsDetails) {
|
||||
return "No problems detected."
|
||||
}
|
||||
|
||||
return diagnosticsDetails
|
||||
}
|
||||
|
||||
@@ -18,6 +18,17 @@ export async function extractTextFromFile(filePath: string): Promise<string> {
|
||||
return extractTextFromDOCX(filePath)
|
||||
case ".ipynb":
|
||||
return extractTextFromIPYNB(filePath)
|
||||
case ".jpg":
|
||||
case ".jpeg":
|
||||
case ".png":
|
||||
case ".gif":
|
||||
case ".webp":
|
||||
case ".mp4":
|
||||
case ".mp3":
|
||||
case ".wav":
|
||||
case ".avi":
|
||||
case ".mov":
|
||||
return "Cannot read media file."
|
||||
default:
|
||||
return await fs.readFile(filePath, "utf8")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user