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
14 changes: 12 additions & 2 deletions app/components/chat/Artifact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ const ActionList = memo(({ actions }: ActionListProps) => {
<div className="i-ph:circle-duotone"></div>
) : status === 'complete' ? (
<div className="i-ph:check"></div>
) : status === 'failed' || status === 'aborted' ? (
) : status === 'aborted' || status === 'failed' ? (
<div className="i-ph:x"></div>
) : null}
</div>
Expand All @@ -239,6 +239,16 @@ const ActionList = memo(({ actions }: ActionListProps) => {
{action.filePath}
</code>
</div>
) : type === 'edit' ? (
<div>
Edit{' '}
<code
className="bg-bolt-elements-artifacts-inlineCode-background text-bolt-elements-artifacts-inlineCode-text px-1.5 py-1 rounded-md text-bolt-elements-item-contentAccent hover:underline cursor-pointer"
onClick={() => openArtifactInWorkbench(action.filePath)}
>
{action.filePath}
</code>
</div>
) : type === 'shell' ? (
<div className="flex items-center w-full min-h-[28px]">
<span className="flex-1">Run command</span>
Expand All @@ -255,7 +265,7 @@ const ActionList = memo(({ actions }: ActionListProps) => {
</a>
) : null}
</div>
{(type === 'shell' || type === 'start') && (
{(type === 'shell' || type === 'start' || type === 'edit') && (
<ShellCodeBlock
classsName={classNames('mt-1', {
'mb-3.5': !isLast,
Expand Down
70 changes: 16 additions & 54 deletions app/components/chat/ToolInvocations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,14 @@ export const ToolInvocations = memo(({ toolInvocations, toolCallAnnotations, add
<div className="tool-invocation border border-bolt-elements-borderColor flex flex-col overflow-hidden rounded-lg w-full transition-border duration-150">
<div className="flex">
<button
className="flex items-stretch bg-bolt-elements-artifacts-background hover:bg-bolt-elements-artifacts-backgroundHover w-full overflow-hidden"
className="flex items-stretch bg-bolt-elements-background-depth-2 hover:bg-bolt-elements-artifacts-backgroundHover w-full overflow-hidden"
onClick={toggleDetails}
aria-label={showDetails ? 'Collapse details' : 'Expand details'}
>
<div className="p-2.5">
<div className="i-ph:wrench text-xl text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary transition-colors"></div>
</div>
<div className="border-l border-bolt-elements-borderColor p-2.5 w-full text-left">
<div className="p-2.5 w-full text-left">
<div className="w-full text-bolt-elements-textPrimary font-medium leading-5 text-sm">
MCP Tool Invocations{' '}
{hasToolResults && (
Expand Down Expand Up @@ -153,7 +153,7 @@ export const ToolInvocations = memo(({ toolInvocations, toolCallAnnotations, add
>
<div className="bg-bolt-elements-artifacts-borderColor h-[1px]" />

<div className="px-3 py-3 text-left bg-bolt-elements-actions-background">
<div className="px-3 py-3 text-left bg-bolt-elements-background-depth-2">
<ToolCallsList
toolInvocations={toolCalls}
toolCallAnnotations={toolCallAnnotations}
Expand Down Expand Up @@ -273,19 +273,12 @@ interface ToolCallsListProps {
theme: Theme;
}

const ToolCallsList = memo(({ toolInvocations, toolCallAnnotations, addToolResult, theme }: ToolCallsListProps) => {
const ToolCallsList = memo(({ toolInvocations, toolCallAnnotations, addToolResult }: ToolCallsListProps) => {
const [expanded, setExpanded] = useState<{ [id: string]: boolean }>({});

// OS detection for shortcut display
const isMac = typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform);

const toggleExpand = (toolCallId: string) => {
setExpanded((prev) => ({
...prev,
[toolCallId]: !prev[toolCallId],
}));
};

useEffect(() => {
const expandedState: { [id: string]: boolean } = {};
toolInvocations.forEach((inv) => {
Expand Down Expand Up @@ -360,51 +353,20 @@ const ToolCallsList = memo(({ toolInvocations, toolCallAnnotations, addToolResul
animate="visible"
transition={{ duration: 0.2, ease: cubicEasingFn }}
>
<div className="">
<div key={toolCallId} className="flex flex-col gap-1">
<div className="flex items-center text-bolt-elements-textSecondary font-semibold text-sm">
<button
onClick={() => toggleExpand(toolCallId)}
className="mr-1 focus:outline-none bg-transparent"
aria-label={expanded[toolCallId] ? 'Collapse' : 'Expand'}
tabIndex={0}
type="button"
>
<span
className={`i-ph:caret-down-bold inline-block transition-transform duration-150 ${expanded[toolCallId] ? '' : '-rotate-90'}`}
/>
</button>
Calling MCP tool{' '}
<span className="ml-0.5 font-light font-mono text-bolt-elements-textPrimary bg-bolt-elements-background-depth-3 px-1.5 py-0.5 rounded-md">
<div className="bg-bolt-elements-background-depth-3 rounded-lg p-2">
<div key={toolCallId} className="flex gap-1">
<div className="flex flex-col items-center ">
<span className="mr-auto font-light font-normal text-md text-bolt-elements-textPrimary rounded-md">
{toolName}
</span>
<span className="text-xs text-bolt-elements-textSecondary font-light break-words max-w-64">
{annotation?.toolDescription}
</span>
</div>
{expanded[toolCallId] && (
<div className="flex gap-3">
<div className="w-[0.1px] min-h-[40px] bg-bolt-elements-background-depth-3 ml-1.5" />
<div className="flex flex-col gap-1 w-full">
<div className="text-bolt-elements-textSecondary text-xs mb-1">
Description:{' '}
<span className="text-bolt-elements-textPrimary font-semibold">
{annotation?.toolDescription}
</span>
</div>
<div className="flex w-full items-stretch space-x-2">
<div className="w-full rounded-md bg-bolt-elements-background-depth-3 p-3 ml-0 border-l-2 border-bolt-elements-borderColor">
<JsonCodeBlock
className="mb-0"
code={JSON.stringify(tool.toolInvocation.args, null, 2)}
theme={theme}
/>
</div>
</div>
</div>
</div>
)}
<div className="flex justify-end gap-2 pt-2.5">
<div className="flex items-center justify-end gap-2 ml-auto">
<button
className={classNames(
'px-2.5 py-1.5 rounded-lg text-xs',
'h-10 px-2.5 py-1.5 rounded-lg text-xs h-auto',
'bg-transparent',
'text-bolt-elements-textTertiary hover:text-bolt-elements-textPrimary',
'transition-all duration-200',
Expand All @@ -421,9 +383,9 @@ const ToolCallsList = memo(({ toolInvocations, toolCallAnnotations, addToolResul
</button>
<button
className={classNames(
'inline-flex items-center gap-2 px-3.5 py-1.5 text-xs font-normal rounded-lg transition-colors',
'bg-accent-500 hover:bg-accent-600',
'text-black',
'h-10 inline-flex items-center gap-2 px-3 py-1.5 text-xs font-normal rounded-lg transition-colors',
'bg-bolt-elements-background-depth-2 border border-bolt-elements-borderColor',
'text-accent-500 hover:text-bolt-elements-textPrimary',
'disabled:opacity-50 disabled:cursor-not-allowed',
)}
onClick={() =>
Expand Down
2 changes: 1 addition & 1 deletion app/lib/.server/llm/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function createFilesContext(files: FileMap, useRelativePath?: boolean) {

const codeWithLinesNumbers = dirent.content
.split('\n')
// .map((v, i) => `${i + 1}|${v}`)
.map((v, i) => `${i + 1}|${v}`)
.join('\n');

let filePath = path;
Expand Down
118 changes: 118 additions & 0 deletions app/lib/common/prompts/new-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,98 @@ The year is 2025.
- shell: Running commands (use --yes for npx/npm create, && for sequences, NEVER re-run dev servers)
- start: Starting project (use ONLY for project startup, LAST action)
- file: Creating/updating files (add filePath and contentType attributes)
- edit: Modifying existing files with diffs (add filePath and contentType attributes)

File Action Rules:
- Only include new/modified files
- ALWAYS add contentType attribute
- NEVER use diffs for new files or SQL migrations
- FORBIDDEN: Binary files, base64 assets



<file_editing_instructions>
Your goal is to produce accurate, clean diff patches that apply seamlessly to existing files. Ensure the changes are logically grouped, correctly formatted, and maintain consistency throughout the file.

For each file that you edit, write out the changes similar to a unified diff like 'diff -u' would produce.
CRITICAL: ONLY diff content should be provided, no other text.

General Principles:
- Changes should be atomic and logically related
- Avoid making any unrelated modifications
- Include diffType="unified" attribute
- Format: <boltAction type="edit" filePath="path/to/file.js" diffType="unified">

Hunk Organization Guidelines:
- Before writing any hunks, think step-by-step and plan all necessary changes holistically
- Changes within a hunk must be logically related—never jump between unrelated parts of the file
- If moving code, delete it from the original location first, then add it to the new location

Specific Formatting Rules:
- Start each hunk with '@@ .. @@'
- NEVER include line numbers or timestamps, these are not needed for the user's patch tool
- CRITICAL: You MUST PREFIX unchanged lines with a single space (' ') to ensure the user's patch tool can interpret them correctly as context lines
- Use '-' to indicate lines to be removed and '+' for lines to be added
- Indentation and whitespace MUST match EXACTLY
- You MUST include full lines of code; DO NOT include partial lines
- EVERY line in the diff must start with either ' ' (context), '-' (removal), or '+' (addition)

Providing Enough Context:
- Always provide sufficient context lines around your changes to ensure they apply correctly
- Context lines are CRUCIAL for guaranteeing that your diff integrates seamlessly
- Include as many unchanged lines as necessary to clearly identify where the edits belong and to avoid ambiguity during patching
- The goal is to provide enough context so that the change can be applied even if minor
- Example of correct context lines:
@@ -5,6 +5,7 @@
import React from 'react'
import { Camera, Heart, Star } from 'lucide-react'
+import { CheckCircle, Mail, MapPin, Phone } from 'lucide-react'

function App() {
return (

Handling Blocks of Code:
- When editing any code block (function, class, loop, component, etc.), choose ONE of these approaches:

1. If only modifying the internal content (block structure remains the same):

\`\`\`
@@ .. @@
if (condition) {
- doSomething();
- doSomethingElse();
+ doSomethingBetter();
}
\`\`\`

2. If changing the block structure, replace the ENTIRE block:

\`\`\`
@@ .. @@
-if (condition) {
- doSomething();
- doSomethingElse();
-}
+if (condition && otherCondition) {
+ doSomethingBetter();
+} else {
+ handleError();
+}
\`\`\`

- NEVER leave a block partially complete - always include matching opening/closing brackets or make sure they are preserved
- When replacing an entire block, you MUST include ALL lines from opening to closing brackets

Common Pitfalls to Avoid:
- Do NOT introduce unnecessary hunks
- NEVER duplicate imports
- ALWAYS maintain existing formatting and indentation
- Ensure each diff applies cleanly without causing compilation or runtime errors

Consistency and Completeness:
- Check for any related changes that need to be made to ensure consistency (e.g., changing all occurrences of a renamed variable)
</file_editing_instructions>

Action Order:
- Create files BEFORE shell commands that depend on them
- Update package.json FIRST, then install dependencies
Expand Down Expand Up @@ -297,6 +382,39 @@ npm run dev

The development server is now running. Ready for your next instructions.</assistant_response>
</example>
<example>
<user_query>Add a console.log statement to the main.js file</user_query>
<assistant_response>I'll add a console.log statement to the main.js file using the edit action.

<boltArtifact id="add-console-log" title="Add console.log to main.js">
<boltAction type="edit" filePath="main.js" diffType="unified">
@@ -1,3 +1,4 @@
import './style.css'
import javascriptLogo from './javascript.svg'
+console.log('Hello from main.js!')
document.querySelector('#app').innerHTML = \`
</boltAction>
</boltArtifact>

The console.log statement has been added to main.js.</assistant_response>
</example>
<example>
<user_query>Replace the console.log with a different message</user_query>
<assistant_response>I'll replace the console.log message in the main.js file.

<boltArtifact id="update-console-log" title="Update console.log message">
<boltAction type="edit" filePath="main.js" diffType="unified">
@@ -1,4 +1,4 @@
import './style.css'
import javascriptLogo from './javascript.svg'
-console.log('Hello from main.js!')
+console.log('Updated message from main.js!')
document.querySelector('#app').innerHTML = \`
</boltAction>
</boltArtifact>

The console.log message has been updated.</assistant_response>
</example>
</examples>`;

export const CONTINUE_PROMPT = stripIndents`
Expand Down
4 changes: 2 additions & 2 deletions app/lib/hooks/useMessageParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ const messageParser = new StreamingMessageParser({
logger.trace('onActionOpen', data.action);

// we only add shell actions when when the close tag got parsed because only then we have the content
if (data.action.type === 'file') {
if (data.action.type === 'file' || data.action.type === 'edit') {
workbenchStore.addAction(data);
}
},
onActionClose: (data) => {
logger.trace('onActionClose', data.action);

if (data.action.type !== 'file') {
if (data.action.type !== 'file' && data.action.type !== 'edit') {
workbenchStore.addAction(data);
}

Expand Down
Loading