diff --git a/app/components/chat/GitCloneButton.tsx b/app/components/chat/GitCloneButton.tsx
index 4300897..bc98924 100644
--- a/app/components/chat/GitCloneButton.tsx
+++ b/app/components/chat/GitCloneButton.tsx
@@ -1,7 +1,7 @@
import ignore from 'ignore';
import { useGit } from '~/lib/hooks/useGit';
import type { Message } from 'ai';
-import { detectProjectCommands, createCommandsMessage } from '~/utils/projectCommands';
+import { detectProjectCommands, createCommandsMessage, escapeBoltTags } from '~/utils/projectCommands';
import { generateId } from '~/utils/fileUtils';
import { useState } from 'react';
import { toast } from 'react-toastify';
@@ -84,7 +84,7 @@ ${fileContents
.map(
(file) =>
`
-${file.content}
+${escapeBoltTags(file.content)}
`,
)
.join('\n')}
diff --git a/app/components/git/GitUrlImport.client.tsx b/app/components/git/GitUrlImport.client.tsx
index 578c35f..9085370 100644
--- a/app/components/git/GitUrlImport.client.tsx
+++ b/app/components/git/GitUrlImport.client.tsx
@@ -7,7 +7,7 @@ import { BaseChat } from '~/components/chat/BaseChat';
import { Chat } from '~/components/chat/Chat.client';
import { useGit } from '~/lib/hooks/useGit';
import { useChatHistory } from '~/lib/persistence';
-import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands';
+import { createCommandsMessage, detectProjectCommands, escapeBoltTags } from '~/utils/projectCommands';
import { LoadingOverlay } from '~/components/ui/LoadingOverlay';
import { toast } from 'react-toastify';
@@ -74,12 +74,12 @@ export function GitUrlImport() {
const filesMessage: Message = {
role: 'assistant',
content: `Cloning the repo ${repoUrl} into ${workdir}
-
+
${fileContents
.map(
(file) =>
`
-${file.content}
+${escapeBoltTags(file.content)}
`,
)
.join('\n')}
diff --git a/app/lib/runtime/message-parser.ts b/app/lib/runtime/message-parser.ts
index 2757179..3b41b6d 100644
--- a/app/lib/runtime/message-parser.ts
+++ b/app/lib/runtime/message-parser.ts
@@ -64,6 +64,10 @@ function cleanoutMarkdownSyntax(content: string) {
return content;
}
}
+
+function cleanEscapedTags(content: string) {
+ return content.replace(/</g, '<').replace(/>/g, '>');
+}
export class StreamingMessageParser {
#messages = new Map();
@@ -110,6 +114,7 @@ export class StreamingMessageParser {
// Remove markdown code block syntax if present and file is not markdown
if (!currentAction.filePath.endsWith('.md')) {
content = cleanoutMarkdownSyntax(content);
+ content = cleanEscapedTags(content);
}
content += '\n';
@@ -141,6 +146,7 @@ export class StreamingMessageParser {
if (!currentAction.filePath.endsWith('.md')) {
content = cleanoutMarkdownSyntax(content);
+ content = cleanEscapedTags(content);
}
this._options.callbacks?.onActionStream?.({
diff --git a/app/utils/folderImport.ts b/app/utils/folderImport.ts
index 759df10..98bbe31 100644
--- a/app/utils/folderImport.ts
+++ b/app/utils/folderImport.ts
@@ -1,6 +1,6 @@
import type { Message } from 'ai';
import { generateId } from './fileUtils';
-import { detectProjectCommands, createCommandsMessage } from './projectCommands';
+import { detectProjectCommands, createCommandsMessage, escapeBoltTags } from './projectCommands';
export const createChatFromFolder = async (
files: File[],
@@ -42,7 +42,7 @@ export const createChatFromFolder = async (
${fileArtifacts
.map(
(file) => `
-${file.content}
+${escapeBoltTags(file.content)}
`,
)
.join('\n\n')}
diff --git a/app/utils/projectCommands.ts b/app/utils/projectCommands.ts
index 050663a..e734dff 100644
--- a/app/utils/projectCommands.ts
+++ b/app/utils/projectCommands.ts
@@ -78,3 +78,39 @@ ${commands.setupCommand}
createdAt: new Date(),
};
}
+
+export function escapeBoltArtifactTags(input: string) {
+ // Regular expression to match boltArtifact tags and their content
+ const regex = /(]*>)([\s\S]*?)(<\/boltArtifact>)/g;
+
+ return input.replace(regex, (match, openTag, content, closeTag) => {
+ // Escape the opening tag
+ const escapedOpenTag = openTag.replace(//g, '>');
+
+ // Escape the closing tag
+ const escapedCloseTag = closeTag.replace(//g, '>');
+
+ // Return the escaped version
+ return `${escapedOpenTag}${content}${escapedCloseTag}`;
+ });
+}
+
+export function escapeBoltAActionTags(input: string) {
+ // Regular expression to match boltArtifact tags and their content
+ const regex = /(]*>)([\s\S]*?)(<\/boltAction>)/g;
+
+ return input.replace(regex, (match, openTag, content, closeTag) => {
+ // Escape the opening tag
+ const escapedOpenTag = openTag.replace(//g, '>');
+
+ // Escape the closing tag
+ const escapedCloseTag = closeTag.replace(//g, '>');
+
+ // Return the escaped version
+ return `${escapedOpenTag}${content}${escapedCloseTag}`;
+ });
+}
+
+export function escapeBoltTags(input: string) {
+ return escapeBoltArtifactTags(escapeBoltAActionTags(input));
+}