mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -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 { truncateHalfConversation } from "./utils/context-management"
|
||||||
import { extractTextFromFile } from "./utils/extract-text"
|
import { extractTextFromFile } from "./utils/extract-text"
|
||||||
import { regexSearchFiles } from "./utils/ripgrep"
|
import { regexSearchFiles } from "./utils/ripgrep"
|
||||||
|
import { parseMentions } from "./utils/context-mentions"
|
||||||
|
|
||||||
const SYSTEM_PROMPT =
|
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.
|
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) {
|
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
|
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 }
|
const result = { response: this.askResponse!, text: this.askResponseText, images: this.askResponseImages }
|
||||||
this.askResponse = undefined
|
this.askResponse = undefined
|
||||||
this.askResponseText = undefined
|
this.askResponseText = undefined
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
|
|||||||
private view?: vscode.WebviewView | vscode.WebviewPanel
|
private view?: vscode.WebviewView | vscode.WebviewPanel
|
||||||
private claudeDev?: ClaudeDev
|
private claudeDev?: ClaudeDev
|
||||||
private workspaceTracker?: WorkspaceTracker
|
private workspaceTracker?: WorkspaceTracker
|
||||||
private urlScraper?: UrlScraper
|
urlScraper?: UrlScraper
|
||||||
private latestAnnouncementId = "sep-14-2024" // update to some unique identifier when we add a new announcement
|
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) {
|
constructor(readonly context: vscode.ExtensionContext, private readonly outputChannel: vscode.OutputChannel) {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import * as vscode from "vscode"
|
import * as vscode from "vscode"
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
import { openFile } from "./open-file"
|
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 {
|
export function openMention(mention?: string): void {
|
||||||
if (!mention) {
|
if (!mention) {
|
||||||
@@ -26,3 +30,126 @@ export function openMention(mention?: string): void {
|
|||||||
vscode.env.openExternal(vscode.Uri.parse(mention))
|
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)
|
return extractTextFromDOCX(filePath)
|
||||||
case ".ipynb":
|
case ".ipynb":
|
||||||
return extractTextFromIPYNB(filePath)
|
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:
|
default:
|
||||||
return await fs.readFile(filePath, "utf8")
|
return await fs.readFile(filePath, "utf8")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user