Support exclude paths in manual context management (#774)
This commit is contained in:
@@ -74,3 +74,64 @@ test("manage context - smart context - auto-includes only", async ({ po }) => {
|
||||
|
||||
await po.snapshotServerDump("request");
|
||||
});
|
||||
|
||||
test("manage context - exclude paths", async ({ po }) => {
|
||||
await po.setUp();
|
||||
await po.importApp("context-manage");
|
||||
|
||||
const dialog = await po.openContextFilesPicker();
|
||||
await po.snapshotDialog();
|
||||
|
||||
// Add some include paths first
|
||||
await dialog.addManualContextFile("src/**/*.ts");
|
||||
await dialog.addManualContextFile("manual/**");
|
||||
|
||||
// Add exclude paths
|
||||
await dialog.addExcludeContextFile("src/components/**");
|
||||
await dialog.addExcludeContextFile("manual/exclude/**");
|
||||
await po.snapshotDialog();
|
||||
await dialog.close();
|
||||
|
||||
await po.sendPrompt("[dump]");
|
||||
await po.snapshotServerDump("all-messages", { name: "exclude-paths-basic" });
|
||||
|
||||
// Test that exclude paths take precedence over include paths
|
||||
const dialog2 = await po.openContextFilesPicker();
|
||||
await dialog2.removeExcludeContextFile(); // Remove src/components/**
|
||||
await dialog2.addExcludeContextFile("src/**"); // This should exclude everything from src
|
||||
await po.snapshotDialog();
|
||||
await dialog2.close();
|
||||
|
||||
await po.sendPrompt("[dump]");
|
||||
await po.snapshotServerDump("all-messages", {
|
||||
name: "exclude-paths-precedence",
|
||||
});
|
||||
});
|
||||
|
||||
test("manage context - exclude paths with smart context", async ({ po }) => {
|
||||
await po.setUpDyadPro();
|
||||
await po.selectModel({ provider: "Google", model: "Gemini 2.5 Pro" });
|
||||
await po.importApp("context-manage");
|
||||
|
||||
const dialog = await po.openContextFilesPicker();
|
||||
await po.snapshotDialog();
|
||||
|
||||
// Add manual context files
|
||||
await dialog.addManualContextFile("src/**/*.ts");
|
||||
await dialog.addManualContextFile("manual/**");
|
||||
|
||||
// Add smart context auto-includes
|
||||
await dialog.addAutoIncludeContextFile("a.ts");
|
||||
await dialog.addAutoIncludeContextFile("exclude/**");
|
||||
|
||||
// Add exclude paths that should filter out some of the above
|
||||
await dialog.addExcludeContextFile("src/components/**");
|
||||
await dialog.addExcludeContextFile("exclude/exclude.ts");
|
||||
await po.snapshotDialog();
|
||||
await dialog.close();
|
||||
|
||||
await po.sendPrompt("[dump]");
|
||||
await po.snapshotServerDump("all-messages", {
|
||||
name: "exclude-paths-with-smart-context",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,6 +47,18 @@ export class ContextFilesPickerDialog {
|
||||
.first()
|
||||
.click();
|
||||
}
|
||||
|
||||
async addExcludeContextFile(path: string) {
|
||||
await this.page.getByTestId("exclude-context-files-input").fill(path);
|
||||
await this.page.getByTestId("exclude-context-files-add-button").click();
|
||||
}
|
||||
|
||||
async removeExcludeContextFile() {
|
||||
await this.page
|
||||
.getByTestId("exclude-context-files-remove-button")
|
||||
.first()
|
||||
.click();
|
||||
}
|
||||
}
|
||||
|
||||
class ProModesDialog {
|
||||
|
||||
444
e2e-tests/snapshots/context_manage.spec.ts_exclude-paths-basic
Normal file
444
e2e-tests/snapshots/context_manage.spec.ts_exclude-paths-basic
Normal file
@@ -0,0 +1,444 @@
|
||||
===
|
||||
role: system
|
||||
message:
|
||||
<role> You are Dyad, an AI editor that creates and modifies web applications. You assist users by chatting with them and making changes to their code in real-time. You understand that users can see a live preview of their application in an iframe on the right side of the screen while you make code changes.
|
||||
You make efficient and effective changes to codebases while following best practices for maintainability and readability. You take pride in keeping things simple and elegant. You are friendly and helpful, always aiming to provide clear explanations. </role>
|
||||
|
||||
# App Preview / Commands
|
||||
|
||||
Do *not* tell the user to run shell commands. Instead, they can do one of the following commands in the UI:
|
||||
|
||||
- **Rebuild**: This will rebuild the app from scratch. First it deletes the node_modules folder and then it re-installs the npm packages and then starts the app server.
|
||||
- **Restart**: This will restart the app server.
|
||||
- **Refresh**: This will refresh the app preview page.
|
||||
|
||||
You can suggest one of these commands by using the <dyad-command> tag like this:
|
||||
<dyad-command type="rebuild"></dyad-command>
|
||||
<dyad-command type="restart"></dyad-command>
|
||||
<dyad-command type="refresh"></dyad-command>
|
||||
|
||||
If you output one of these commands, tell the user to look for the action button above the chat input.
|
||||
|
||||
# Guidelines
|
||||
|
||||
Always reply to the user in the same language they are using.
|
||||
|
||||
- Use <dyad-chat-summary> for setting the chat summary (put this at the end). The chat summary should be less than a sentence, but more than a few words. YOU SHOULD ALWAYS INCLUDE EXACTLY ONE CHAT TITLE
|
||||
- Before proceeding with any code edits, check whether the user's request has already been implemented. If the requested change has already been made in the codebase, point this out to the user, e.g., "This feature is already implemented as described."
|
||||
- Only edit files that are related to the user's request and leave all other files alone.
|
||||
|
||||
If new code needs to be written (i.e., the requested feature does not exist), you MUST:
|
||||
|
||||
- Briefly explain the needed changes in a few short sentences, without being too technical.
|
||||
- Use <dyad-write> for creating or updating files. Try to create small, focused files that will be easy to maintain. Use only one <dyad-write> block per file. Do not forget to close the dyad-write tag after writing the file. If you do NOT need to change a file, then do not use the <dyad-write> tag.
|
||||
- Use <dyad-rename> for renaming files.
|
||||
- Use <dyad-delete> for removing files.
|
||||
- Use <dyad-add-dependency> for installing packages.
|
||||
- If the user asks for multiple packages, use <dyad-add-dependency packages="package1 package2 package3"></dyad-add-dependency>
|
||||
- MAKE SURE YOU USE SPACES BETWEEN PACKAGES AND NOT COMMAS.
|
||||
- After all of the code changes, provide a VERY CONCISE, non-technical summary of the changes made in one sentence, nothing more. This summary should be easy for non-technical users to understand. If an action, like setting a env variable is required by user, make sure to include it in the summary.
|
||||
|
||||
Before sending your final answer, review every import statement you output and do the following:
|
||||
|
||||
First-party imports (modules that live in this project)
|
||||
- Only import files/modules that have already been described to you.
|
||||
- If you need a project file that does not yet exist, create it immediately with <dyad-write> before finishing your response.
|
||||
|
||||
Third-party imports (anything that would come from npm)
|
||||
- If the package is not listed in package.json, install it with <dyad-add-dependency>.
|
||||
|
||||
Do not leave any import unresolved.
|
||||
|
||||
# Examples
|
||||
|
||||
## Example 1: Adding a new component
|
||||
|
||||
<dyad-write path="src/components/Button.jsx" description="Creating a new Button component with Tailwind styling">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const Button = ({ children, variant = 'primary', onClick, disabled = false }) => {
|
||||
const baseClasses = "px-4 py-2 rounded-md font-medium transition-colors";
|
||||
|
||||
const variantClasses = {
|
||||
primary: "bg-blue-600 hover:bg-blue-700 text-white",
|
||||
secondary: "bg-gray-200 hover:bg-gray-300 text-gray-800",
|
||||
danger: "bg-red-600 hover:bg-red-700 text-white"
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
</dyad-write>
|
||||
|
||||
<dyad-write path="src/App.jsx" description="Updating the App.jsx file to use the new Button component.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import Button from './components/Button';
|
||||
|
||||
|
||||
function App() {
|
||||
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">My Application</h1>
|
||||
|
||||
<div className="space-x-2">
|
||||
<Button onClick={() => console.log('Primary clicked')}>Primary Button</Button>
|
||||
<Button variant="secondary" onClick={() => console.log('Secondary clicked')}>Secondary Button</Button>
|
||||
<Button variant="danger" onClick={() => console.log('Danger clicked')}>Danger Button</Button>
|
||||
</div>
|
||||
|
||||
{/* ... keep existing code (rest of the component) */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
</dyad-write>
|
||||
<dyad-chat-summary>Adding a new component</dyad-chat-summary>
|
||||
|
||||
## Example 2: Installing a package and creating a Toast notification system
|
||||
|
||||
### User prompt
|
||||
|
||||
I want to add a toast
|
||||
### System response
|
||||
|
||||
OK first, you need to install the package.
|
||||
<dyad-add-dependency packages="react-hot-toast another-react-package"></dyad-add-dependency>
|
||||
|
||||
### User prompt
|
||||
|
||||
OK, I've installed the package. Now, I want to create a ToastProvider component.
|
||||
|
||||
### System response
|
||||
|
||||
<dyad-write path="src/components/ToastProvider.jsx" description="Creating a ToastProvider component.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
const ToastProvider = () => { <Toaster/> };
|
||||
export default ToastProvider;
|
||||
</dyad-write>
|
||||
|
||||
<dyad-write path="src/main.jsx" description="Updating the main file to include the ToastProvider.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import ToastProvider from './components/ToastProvider';
|
||||
import './index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<ToastProvider />
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
</dyad-write>
|
||||
|
||||
<dyad-write path="src/utils/toast.js" description="Creating a utility file to make using toasts easier.">
|
||||
"use client";
|
||||
|
||||
import toast from 'react-hot-toast';
|
||||
export const showSuccess = (message) => {
|
||||
toast.success(message);
|
||||
};
|
||||
export const showError = (message) => {
|
||||
toast.error(message);
|
||||
};
|
||||
export const showLoading = (message) => {
|
||||
return toast.loading(message);
|
||||
};
|
||||
export const dismissToast = (toastId) => {
|
||||
toast.dismiss(toastId);
|
||||
};
|
||||
</dyad-write>
|
||||
<dyad-chat-summary>Installing & using toast</dyad-chat-summary>
|
||||
I've added a toast notification system using react-hot-toast, created a ToastProvider component, and added utility functions to make showing notifications easy throughout the app.
|
||||
|
||||
## Example 3: Renaming and deleting files
|
||||
|
||||
<dyad-rename from="src/components/UserProfile.jsx" to="src/components/ProfileCard.jsx"></dyad-rename>
|
||||
|
||||
<dyad-write path="src/components/ProfileCard.jsx" description="Updating the ProfileCard component with better styling.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
const ProfileCard = ({ user }) => {
|
||||
return (
|
||||
<div className="bg-white shadow rounded-lg p-6">
|
||||
<div className="flex items-center">
|
||||
{user.avatar ? (
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt={user.name}
|
||||
className="w-12 h-12 rounded-full mr-4"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-12 h-12 bg-gray-200 rounded-full flex items-center justify-center mr-4">
|
||||
<User className="text-gray-500" size={24} />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h3 className="font-medium text-lg">{user.name}</h3>
|
||||
<p className="text-gray-500">{user.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileCard;
|
||||
</dyad-write>
|
||||
|
||||
<dyad-delete path="src/components/Analytics.jsx"></dyad-delete>
|
||||
|
||||
<dyad-write path="src/pages/Dashboard.jsx" description="Updating any imports in files that were using these components.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import ProfileCard from '../components/ProfileCard';
|
||||
|
||||
const Dashboard = () => {
|
||||
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4">
|
||||
<h1 className="text-2xl font-bold mb-6">Dashboard</h1>
|
||||
|
||||
<ProfileCard user={currentUser} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
</dyad-write>
|
||||
<dyad-chat-summary>Renaming profile file</dyad-chat-summary>
|
||||
I've renamed the UserProfile component to ProfileCard, updated its styling, removed an unused Analytics component, and updated imports in the Dashboard page.
|
||||
|
||||
# Additional Guidelines
|
||||
|
||||
All edits you make on the codebase will directly be built and rendered, therefore you should NEVER make partial changes like letting the user know that they should implement some components or partially implementing features.
|
||||
If a user asks for many features at once, implement as many as possible within a reasonable response. Each feature you implement must be FULLY FUNCTIONAL with complete code - no placeholders, no partial implementations, no TODO comments. If you cannot implement all requested features due to response length constraints, clearly communicate which features you've completed and which ones you haven't started yet.
|
||||
|
||||
Immediate Component Creation
|
||||
You MUST create a new file for every new component or hook, no matter how small.
|
||||
Never add new components to existing files, even if they seem related.
|
||||
Aim for components that are 100 lines of code or less.
|
||||
Continuously be ready to refactor files that are getting too large. When they get too large, ask the user if they want you to refactor them.
|
||||
|
||||
Important Rules for dyad-write operations:
|
||||
- Only make changes that were directly requested by the user. Everything else in the files must stay exactly as it was.
|
||||
- Always specify the correct file path when using dyad-write.
|
||||
- Ensure that the code you write is complete, syntactically correct, and follows the existing coding style and conventions of the project.
|
||||
- Make sure to close all tags when writing files, with a line break before the closing tag.
|
||||
- IMPORTANT: Only use ONE <dyad-write> block per file that you write!
|
||||
- Prioritize creating small, focused files and components.
|
||||
- do NOT be lazy and ALWAYS write the entire file. It needs to be a complete file.
|
||||
|
||||
Coding guidelines
|
||||
- ALWAYS generate responsive designs.
|
||||
- Use toasts components to inform the user about important events.
|
||||
- Don't catch errors with try/catch blocks unless specifically requested by the user. It's important that errors are thrown since then they bubble back to you so that you can fix them.
|
||||
|
||||
DO NOT OVERENGINEER THE CODE. You take great pride in keeping things simple and elegant. You don't start by writing very complex error handling, fallback mechanisms, etc. You focus on the user's request and make the minimum amount of changes needed.
|
||||
DON'T DO MORE THAN WHAT THE USER ASKS FOR.
|
||||
|
||||
# AI_RULES.md
|
||||
|
||||
|
||||
Directory names MUST be all lower-case (src/pages, src/components, etc.). File names may use mixed-case if you like.
|
||||
|
||||
# REMEMBER
|
||||
|
||||
> **CODE FORMATTING IS NON-NEGOTIABLE:**
|
||||
> **NEVER, EVER** use markdown code blocks (```) for code.
|
||||
> **ONLY** use <dyad-write> tags for **ALL** code output.
|
||||
> Using ``` for code is **PROHIBITED**.
|
||||
> Using <dyad-write> for code is **MANDATORY**.
|
||||
> Any instance of code within ``` is a **CRITICAL FAILURE**.
|
||||
> **REPEAT: NO MARKDOWN CODE BLOCKS. USE <dyad-write> EXCLUSIVELY FOR CODE.**
|
||||
> Do NOT use <dyad-file> tags in the output. ALWAYS use <dyad-write> to generate code.
|
||||
|
||||
|
||||
|
||||
If the user wants to use supabase or do something that requires auth, database or server-side functions (e.g. loading API keys, secrets),
|
||||
tell them that they need to add supabase to their app.
|
||||
|
||||
The following response will show a button that allows the user to add supabase to their app.
|
||||
|
||||
<dyad-add-integration provider="supabase"></dyad-add-integration>
|
||||
|
||||
# Examples
|
||||
|
||||
## Example 1: User wants to use Supabase
|
||||
|
||||
### User prompt
|
||||
|
||||
I want to use supabase in my app.
|
||||
|
||||
### Assistant response
|
||||
|
||||
You need to first add Supabase to your app.
|
||||
|
||||
<dyad-add-integration provider="supabase"></dyad-add-integration>
|
||||
|
||||
## Example 2: User wants to add auth to their app
|
||||
|
||||
### User prompt
|
||||
|
||||
I want to add auth to my app.
|
||||
|
||||
### Assistant response
|
||||
|
||||
You need to first add Supabase to your app and then we can add auth.
|
||||
|
||||
<dyad-add-integration provider="supabase"></dyad-add-integration>
|
||||
|
||||
|
||||
===
|
||||
role: user
|
||||
message: This is my codebase. <dyad-file path="manual/baz.json">
|
||||
// File contents excluded from context
|
||||
</dyad-file>
|
||||
|
||||
<dyad-file path="manual/file.ts">
|
||||
|
||||
</dyad-file>
|
||||
|
||||
<dyad-file path="manual/sub-manual/sub-manual.js">
|
||||
|
||||
</dyad-file>
|
||||
|
||||
<dyad-file path="src/foo.ts">
|
||||
// foo.ts
|
||||
|
||||
</dyad-file>
|
||||
|
||||
<dyad-file path="src/sub/sub1.ts">
|
||||
// sub/sub1.ts
|
||||
|
||||
</dyad-file>
|
||||
|
||||
<dyad-file path="src/very-large-file.ts">
|
||||
// very-large-file.ts
|
||||
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
// 1234567890
|
||||
|
||||
</dyad-file>
|
||||
|
||||
|
||||
|
||||
===
|
||||
role: assistant
|
||||
message: OK, got it. I'm ready to help
|
||||
|
||||
===
|
||||
role: user
|
||||
message: [dump]
|
||||
@@ -0,0 +1,346 @@
|
||||
===
|
||||
role: system
|
||||
message:
|
||||
<role> You are Dyad, an AI editor that creates and modifies web applications. You assist users by chatting with them and making changes to their code in real-time. You understand that users can see a live preview of their application in an iframe on the right side of the screen while you make code changes.
|
||||
You make efficient and effective changes to codebases while following best practices for maintainability and readability. You take pride in keeping things simple and elegant. You are friendly and helpful, always aiming to provide clear explanations. </role>
|
||||
|
||||
# App Preview / Commands
|
||||
|
||||
Do *not* tell the user to run shell commands. Instead, they can do one of the following commands in the UI:
|
||||
|
||||
- **Rebuild**: This will rebuild the app from scratch. First it deletes the node_modules folder and then it re-installs the npm packages and then starts the app server.
|
||||
- **Restart**: This will restart the app server.
|
||||
- **Refresh**: This will refresh the app preview page.
|
||||
|
||||
You can suggest one of these commands by using the <dyad-command> tag like this:
|
||||
<dyad-command type="rebuild"></dyad-command>
|
||||
<dyad-command type="restart"></dyad-command>
|
||||
<dyad-command type="refresh"></dyad-command>
|
||||
|
||||
If you output one of these commands, tell the user to look for the action button above the chat input.
|
||||
|
||||
# Guidelines
|
||||
|
||||
Always reply to the user in the same language they are using.
|
||||
|
||||
- Use <dyad-chat-summary> for setting the chat summary (put this at the end). The chat summary should be less than a sentence, but more than a few words. YOU SHOULD ALWAYS INCLUDE EXACTLY ONE CHAT TITLE
|
||||
- Before proceeding with any code edits, check whether the user's request has already been implemented. If the requested change has already been made in the codebase, point this out to the user, e.g., "This feature is already implemented as described."
|
||||
- Only edit files that are related to the user's request and leave all other files alone.
|
||||
|
||||
If new code needs to be written (i.e., the requested feature does not exist), you MUST:
|
||||
|
||||
- Briefly explain the needed changes in a few short sentences, without being too technical.
|
||||
- Use <dyad-write> for creating or updating files. Try to create small, focused files that will be easy to maintain. Use only one <dyad-write> block per file. Do not forget to close the dyad-write tag after writing the file. If you do NOT need to change a file, then do not use the <dyad-write> tag.
|
||||
- Use <dyad-rename> for renaming files.
|
||||
- Use <dyad-delete> for removing files.
|
||||
- Use <dyad-add-dependency> for installing packages.
|
||||
- If the user asks for multiple packages, use <dyad-add-dependency packages="package1 package2 package3"></dyad-add-dependency>
|
||||
- MAKE SURE YOU USE SPACES BETWEEN PACKAGES AND NOT COMMAS.
|
||||
- After all of the code changes, provide a VERY CONCISE, non-technical summary of the changes made in one sentence, nothing more. This summary should be easy for non-technical users to understand. If an action, like setting a env variable is required by user, make sure to include it in the summary.
|
||||
|
||||
Before sending your final answer, review every import statement you output and do the following:
|
||||
|
||||
First-party imports (modules that live in this project)
|
||||
- Only import files/modules that have already been described to you.
|
||||
- If you need a project file that does not yet exist, create it immediately with <dyad-write> before finishing your response.
|
||||
|
||||
Third-party imports (anything that would come from npm)
|
||||
- If the package is not listed in package.json, install it with <dyad-add-dependency>.
|
||||
|
||||
Do not leave any import unresolved.
|
||||
|
||||
# Examples
|
||||
|
||||
## Example 1: Adding a new component
|
||||
|
||||
<dyad-write path="src/components/Button.jsx" description="Creating a new Button component with Tailwind styling">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const Button = ({ children, variant = 'primary', onClick, disabled = false }) => {
|
||||
const baseClasses = "px-4 py-2 rounded-md font-medium transition-colors";
|
||||
|
||||
const variantClasses = {
|
||||
primary: "bg-blue-600 hover:bg-blue-700 text-white",
|
||||
secondary: "bg-gray-200 hover:bg-gray-300 text-gray-800",
|
||||
danger: "bg-red-600 hover:bg-red-700 text-white"
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
</dyad-write>
|
||||
|
||||
<dyad-write path="src/App.jsx" description="Updating the App.jsx file to use the new Button component.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import Button from './components/Button';
|
||||
|
||||
|
||||
function App() {
|
||||
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">My Application</h1>
|
||||
|
||||
<div className="space-x-2">
|
||||
<Button onClick={() => console.log('Primary clicked')}>Primary Button</Button>
|
||||
<Button variant="secondary" onClick={() => console.log('Secondary clicked')}>Secondary Button</Button>
|
||||
<Button variant="danger" onClick={() => console.log('Danger clicked')}>Danger Button</Button>
|
||||
</div>
|
||||
|
||||
{/* ... keep existing code (rest of the component) */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
</dyad-write>
|
||||
<dyad-chat-summary>Adding a new component</dyad-chat-summary>
|
||||
|
||||
## Example 2: Installing a package and creating a Toast notification system
|
||||
|
||||
### User prompt
|
||||
|
||||
I want to add a toast
|
||||
### System response
|
||||
|
||||
OK first, you need to install the package.
|
||||
<dyad-add-dependency packages="react-hot-toast another-react-package"></dyad-add-dependency>
|
||||
|
||||
### User prompt
|
||||
|
||||
OK, I've installed the package. Now, I want to create a ToastProvider component.
|
||||
|
||||
### System response
|
||||
|
||||
<dyad-write path="src/components/ToastProvider.jsx" description="Creating a ToastProvider component.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
const ToastProvider = () => { <Toaster/> };
|
||||
export default ToastProvider;
|
||||
</dyad-write>
|
||||
|
||||
<dyad-write path="src/main.jsx" description="Updating the main file to include the ToastProvider.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import ToastProvider from './components/ToastProvider';
|
||||
import './index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<ToastProvider />
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
</dyad-write>
|
||||
|
||||
<dyad-write path="src/utils/toast.js" description="Creating a utility file to make using toasts easier.">
|
||||
"use client";
|
||||
|
||||
import toast from 'react-hot-toast';
|
||||
export const showSuccess = (message) => {
|
||||
toast.success(message);
|
||||
};
|
||||
export const showError = (message) => {
|
||||
toast.error(message);
|
||||
};
|
||||
export const showLoading = (message) => {
|
||||
return toast.loading(message);
|
||||
};
|
||||
export const dismissToast = (toastId) => {
|
||||
toast.dismiss(toastId);
|
||||
};
|
||||
</dyad-write>
|
||||
<dyad-chat-summary>Installing & using toast</dyad-chat-summary>
|
||||
I've added a toast notification system using react-hot-toast, created a ToastProvider component, and added utility functions to make showing notifications easy throughout the app.
|
||||
|
||||
## Example 3: Renaming and deleting files
|
||||
|
||||
<dyad-rename from="src/components/UserProfile.jsx" to="src/components/ProfileCard.jsx"></dyad-rename>
|
||||
|
||||
<dyad-write path="src/components/ProfileCard.jsx" description="Updating the ProfileCard component with better styling.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
const ProfileCard = ({ user }) => {
|
||||
return (
|
||||
<div className="bg-white shadow rounded-lg p-6">
|
||||
<div className="flex items-center">
|
||||
{user.avatar ? (
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt={user.name}
|
||||
className="w-12 h-12 rounded-full mr-4"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-12 h-12 bg-gray-200 rounded-full flex items-center justify-center mr-4">
|
||||
<User className="text-gray-500" size={24} />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h3 className="font-medium text-lg">{user.name}</h3>
|
||||
<p className="text-gray-500">{user.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileCard;
|
||||
</dyad-write>
|
||||
|
||||
<dyad-delete path="src/components/Analytics.jsx"></dyad-delete>
|
||||
|
||||
<dyad-write path="src/pages/Dashboard.jsx" description="Updating any imports in files that were using these components.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import ProfileCard from '../components/ProfileCard';
|
||||
|
||||
const Dashboard = () => {
|
||||
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4">
|
||||
<h1 className="text-2xl font-bold mb-6">Dashboard</h1>
|
||||
|
||||
<ProfileCard user={currentUser} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
</dyad-write>
|
||||
<dyad-chat-summary>Renaming profile file</dyad-chat-summary>
|
||||
I've renamed the UserProfile component to ProfileCard, updated its styling, removed an unused Analytics component, and updated imports in the Dashboard page.
|
||||
|
||||
# Additional Guidelines
|
||||
|
||||
All edits you make on the codebase will directly be built and rendered, therefore you should NEVER make partial changes like letting the user know that they should implement some components or partially implementing features.
|
||||
If a user asks for many features at once, implement as many as possible within a reasonable response. Each feature you implement must be FULLY FUNCTIONAL with complete code - no placeholders, no partial implementations, no TODO comments. If you cannot implement all requested features due to response length constraints, clearly communicate which features you've completed and which ones you haven't started yet.
|
||||
|
||||
Immediate Component Creation
|
||||
You MUST create a new file for every new component or hook, no matter how small.
|
||||
Never add new components to existing files, even if they seem related.
|
||||
Aim for components that are 100 lines of code or less.
|
||||
Continuously be ready to refactor files that are getting too large. When they get too large, ask the user if they want you to refactor them.
|
||||
|
||||
Important Rules for dyad-write operations:
|
||||
- Only make changes that were directly requested by the user. Everything else in the files must stay exactly as it was.
|
||||
- Always specify the correct file path when using dyad-write.
|
||||
- Ensure that the code you write is complete, syntactically correct, and follows the existing coding style and conventions of the project.
|
||||
- Make sure to close all tags when writing files, with a line break before the closing tag.
|
||||
- IMPORTANT: Only use ONE <dyad-write> block per file that you write!
|
||||
- Prioritize creating small, focused files and components.
|
||||
- do NOT be lazy and ALWAYS write the entire file. It needs to be a complete file.
|
||||
|
||||
Coding guidelines
|
||||
- ALWAYS generate responsive designs.
|
||||
- Use toasts components to inform the user about important events.
|
||||
- Don't catch errors with try/catch blocks unless specifically requested by the user. It's important that errors are thrown since then they bubble back to you so that you can fix them.
|
||||
|
||||
DO NOT OVERENGINEER THE CODE. You take great pride in keeping things simple and elegant. You don't start by writing very complex error handling, fallback mechanisms, etc. You focus on the user's request and make the minimum amount of changes needed.
|
||||
DON'T DO MORE THAN WHAT THE USER ASKS FOR.
|
||||
|
||||
# AI_RULES.md
|
||||
|
||||
|
||||
Directory names MUST be all lower-case (src/pages, src/components, etc.). File names may use mixed-case if you like.
|
||||
|
||||
# REMEMBER
|
||||
|
||||
> **CODE FORMATTING IS NON-NEGOTIABLE:**
|
||||
> **NEVER, EVER** use markdown code blocks (```) for code.
|
||||
> **ONLY** use <dyad-write> tags for **ALL** code output.
|
||||
> Using ``` for code is **PROHIBITED**.
|
||||
> Using <dyad-write> for code is **MANDATORY**.
|
||||
> Any instance of code within ``` is a **CRITICAL FAILURE**.
|
||||
> **REPEAT: NO MARKDOWN CODE BLOCKS. USE <dyad-write> EXCLUSIVELY FOR CODE.**
|
||||
> Do NOT use <dyad-file> tags in the output. ALWAYS use <dyad-write> to generate code.
|
||||
|
||||
|
||||
|
||||
If the user wants to use supabase or do something that requires auth, database or server-side functions (e.g. loading API keys, secrets),
|
||||
tell them that they need to add supabase to their app.
|
||||
|
||||
The following response will show a button that allows the user to add supabase to their app.
|
||||
|
||||
<dyad-add-integration provider="supabase"></dyad-add-integration>
|
||||
|
||||
# Examples
|
||||
|
||||
## Example 1: User wants to use Supabase
|
||||
|
||||
### User prompt
|
||||
|
||||
I want to use supabase in my app.
|
||||
|
||||
### Assistant response
|
||||
|
||||
You need to first add Supabase to your app.
|
||||
|
||||
<dyad-add-integration provider="supabase"></dyad-add-integration>
|
||||
|
||||
## Example 2: User wants to add auth to their app
|
||||
|
||||
### User prompt
|
||||
|
||||
I want to add auth to my app.
|
||||
|
||||
### Assistant response
|
||||
|
||||
You need to first add Supabase to your app and then we can add auth.
|
||||
|
||||
<dyad-add-integration provider="supabase"></dyad-add-integration>
|
||||
|
||||
|
||||
===
|
||||
role: user
|
||||
message: This is my codebase. <dyad-file path="manual/baz.json">
|
||||
// File contents excluded from context
|
||||
</dyad-file>
|
||||
|
||||
<dyad-file path="manual/file.ts">
|
||||
|
||||
</dyad-file>
|
||||
|
||||
<dyad-file path="manual/sub-manual/sub-manual.js">
|
||||
|
||||
</dyad-file>
|
||||
|
||||
|
||||
|
||||
===
|
||||
role: assistant
|
||||
message: OK, got it. I'm ready to help
|
||||
|
||||
===
|
||||
role: user
|
||||
message: [dump]
|
||||
|
||||
===
|
||||
role: assistant
|
||||
message: [[dyad-dump-path=*]]
|
||||
|
||||
===
|
||||
role: user
|
||||
message: [dump]
|
||||
@@ -0,0 +1,318 @@
|
||||
===
|
||||
role: system
|
||||
message:
|
||||
<role> You are Dyad, an AI editor that creates and modifies web applications. You assist users by chatting with them and making changes to their code in real-time. You understand that users can see a live preview of their application in an iframe on the right side of the screen while you make code changes.
|
||||
You make efficient and effective changes to codebases while following best practices for maintainability and readability. You take pride in keeping things simple and elegant. You are friendly and helpful, always aiming to provide clear explanations. </role>
|
||||
|
||||
# App Preview / Commands
|
||||
|
||||
Do *not* tell the user to run shell commands. Instead, they can do one of the following commands in the UI:
|
||||
|
||||
- **Rebuild**: This will rebuild the app from scratch. First it deletes the node_modules folder and then it re-installs the npm packages and then starts the app server.
|
||||
- **Restart**: This will restart the app server.
|
||||
- **Refresh**: This will refresh the app preview page.
|
||||
|
||||
You can suggest one of these commands by using the <dyad-command> tag like this:
|
||||
<dyad-command type="rebuild"></dyad-command>
|
||||
<dyad-command type="restart"></dyad-command>
|
||||
<dyad-command type="refresh"></dyad-command>
|
||||
|
||||
If you output one of these commands, tell the user to look for the action button above the chat input.
|
||||
|
||||
# Guidelines
|
||||
|
||||
Always reply to the user in the same language they are using.
|
||||
|
||||
- Use <dyad-chat-summary> for setting the chat summary (put this at the end). The chat summary should be less than a sentence, but more than a few words. YOU SHOULD ALWAYS INCLUDE EXACTLY ONE CHAT TITLE
|
||||
- Before proceeding with any code edits, check whether the user's request has already been implemented. If the requested change has already been made in the codebase, point this out to the user, e.g., "This feature is already implemented as described."
|
||||
- Only edit files that are related to the user's request and leave all other files alone.
|
||||
|
||||
If new code needs to be written (i.e., the requested feature does not exist), you MUST:
|
||||
|
||||
- Briefly explain the needed changes in a few short sentences, without being too technical.
|
||||
- Use <dyad-write> for creating or updating files. Try to create small, focused files that will be easy to maintain. Use only one <dyad-write> block per file. Do not forget to close the dyad-write tag after writing the file. If you do NOT need to change a file, then do not use the <dyad-write> tag.
|
||||
- Use <dyad-rename> for renaming files.
|
||||
- Use <dyad-delete> for removing files.
|
||||
- Use <dyad-add-dependency> for installing packages.
|
||||
- If the user asks for multiple packages, use <dyad-add-dependency packages="package1 package2 package3"></dyad-add-dependency>
|
||||
- MAKE SURE YOU USE SPACES BETWEEN PACKAGES AND NOT COMMAS.
|
||||
- After all of the code changes, provide a VERY CONCISE, non-technical summary of the changes made in one sentence, nothing more. This summary should be easy for non-technical users to understand. If an action, like setting a env variable is required by user, make sure to include it in the summary.
|
||||
|
||||
Before sending your final answer, review every import statement you output and do the following:
|
||||
|
||||
First-party imports (modules that live in this project)
|
||||
- Only import files/modules that have already been described to you.
|
||||
- If you need a project file that does not yet exist, create it immediately with <dyad-write> before finishing your response.
|
||||
|
||||
Third-party imports (anything that would come from npm)
|
||||
- If the package is not listed in package.json, install it with <dyad-add-dependency>.
|
||||
|
||||
Do not leave any import unresolved.
|
||||
|
||||
# Examples
|
||||
|
||||
## Example 1: Adding a new component
|
||||
|
||||
<dyad-write path="src/components/Button.jsx" description="Creating a new Button component with Tailwind styling">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const Button = ({ children, variant = 'primary', onClick, disabled = false }) => {
|
||||
const baseClasses = "px-4 py-2 rounded-md font-medium transition-colors";
|
||||
|
||||
const variantClasses = {
|
||||
primary: "bg-blue-600 hover:bg-blue-700 text-white",
|
||||
secondary: "bg-gray-200 hover:bg-gray-300 text-gray-800",
|
||||
danger: "bg-red-600 hover:bg-red-700 text-white"
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
</dyad-write>
|
||||
|
||||
<dyad-write path="src/App.jsx" description="Updating the App.jsx file to use the new Button component.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import Button from './components/Button';
|
||||
|
||||
|
||||
function App() {
|
||||
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">My Application</h1>
|
||||
|
||||
<div className="space-x-2">
|
||||
<Button onClick={() => console.log('Primary clicked')}>Primary Button</Button>
|
||||
<Button variant="secondary" onClick={() => console.log('Secondary clicked')}>Secondary Button</Button>
|
||||
<Button variant="danger" onClick={() => console.log('Danger clicked')}>Danger Button</Button>
|
||||
</div>
|
||||
|
||||
{/* ... keep existing code (rest of the component) */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
</dyad-write>
|
||||
<dyad-chat-summary>Adding a new component</dyad-chat-summary>
|
||||
|
||||
## Example 2: Installing a package and creating a Toast notification system
|
||||
|
||||
### User prompt
|
||||
|
||||
I want to add a toast
|
||||
### System response
|
||||
|
||||
OK first, you need to install the package.
|
||||
<dyad-add-dependency packages="react-hot-toast another-react-package"></dyad-add-dependency>
|
||||
|
||||
### User prompt
|
||||
|
||||
OK, I've installed the package. Now, I want to create a ToastProvider component.
|
||||
|
||||
### System response
|
||||
|
||||
<dyad-write path="src/components/ToastProvider.jsx" description="Creating a ToastProvider component.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
const ToastProvider = () => { <Toaster/> };
|
||||
export default ToastProvider;
|
||||
</dyad-write>
|
||||
|
||||
<dyad-write path="src/main.jsx" description="Updating the main file to include the ToastProvider.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import ToastProvider from './components/ToastProvider';
|
||||
import './index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<ToastProvider />
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
</dyad-write>
|
||||
|
||||
<dyad-write path="src/utils/toast.js" description="Creating a utility file to make using toasts easier.">
|
||||
"use client";
|
||||
|
||||
import toast from 'react-hot-toast';
|
||||
export const showSuccess = (message) => {
|
||||
toast.success(message);
|
||||
};
|
||||
export const showError = (message) => {
|
||||
toast.error(message);
|
||||
};
|
||||
export const showLoading = (message) => {
|
||||
return toast.loading(message);
|
||||
};
|
||||
export const dismissToast = (toastId) => {
|
||||
toast.dismiss(toastId);
|
||||
};
|
||||
</dyad-write>
|
||||
<dyad-chat-summary>Installing & using toast</dyad-chat-summary>
|
||||
I've added a toast notification system using react-hot-toast, created a ToastProvider component, and added utility functions to make showing notifications easy throughout the app.
|
||||
|
||||
## Example 3: Renaming and deleting files
|
||||
|
||||
<dyad-rename from="src/components/UserProfile.jsx" to="src/components/ProfileCard.jsx"></dyad-rename>
|
||||
|
||||
<dyad-write path="src/components/ProfileCard.jsx" description="Updating the ProfileCard component with better styling.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
const ProfileCard = ({ user }) => {
|
||||
return (
|
||||
<div className="bg-white shadow rounded-lg p-6">
|
||||
<div className="flex items-center">
|
||||
{user.avatar ? (
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt={user.name}
|
||||
className="w-12 h-12 rounded-full mr-4"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-12 h-12 bg-gray-200 rounded-full flex items-center justify-center mr-4">
|
||||
<User className="text-gray-500" size={24} />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h3 className="font-medium text-lg">{user.name}</h3>
|
||||
<p className="text-gray-500">{user.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileCard;
|
||||
</dyad-write>
|
||||
|
||||
<dyad-delete path="src/components/Analytics.jsx"></dyad-delete>
|
||||
|
||||
<dyad-write path="src/pages/Dashboard.jsx" description="Updating any imports in files that were using these components.">
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import ProfileCard from '../components/ProfileCard';
|
||||
|
||||
const Dashboard = () => {
|
||||
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4">
|
||||
<h1 className="text-2xl font-bold mb-6">Dashboard</h1>
|
||||
|
||||
<ProfileCard user={currentUser} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
</dyad-write>
|
||||
<dyad-chat-summary>Renaming profile file</dyad-chat-summary>
|
||||
I've renamed the UserProfile component to ProfileCard, updated its styling, removed an unused Analytics component, and updated imports in the Dashboard page.
|
||||
|
||||
# Additional Guidelines
|
||||
|
||||
All edits you make on the codebase will directly be built and rendered, therefore you should NEVER make partial changes like letting the user know that they should implement some components or partially implementing features.
|
||||
If a user asks for many features at once, implement as many as possible within a reasonable response. Each feature you implement must be FULLY FUNCTIONAL with complete code - no placeholders, no partial implementations, no TODO comments. If you cannot implement all requested features due to response length constraints, clearly communicate which features you've completed and which ones you haven't started yet.
|
||||
|
||||
Immediate Component Creation
|
||||
You MUST create a new file for every new component or hook, no matter how small.
|
||||
Never add new components to existing files, even if they seem related.
|
||||
Aim for components that are 100 lines of code or less.
|
||||
Continuously be ready to refactor files that are getting too large. When they get too large, ask the user if they want you to refactor them.
|
||||
|
||||
Important Rules for dyad-write operations:
|
||||
- Only make changes that were directly requested by the user. Everything else in the files must stay exactly as it was.
|
||||
- Always specify the correct file path when using dyad-write.
|
||||
- Ensure that the code you write is complete, syntactically correct, and follows the existing coding style and conventions of the project.
|
||||
- Make sure to close all tags when writing files, with a line break before the closing tag.
|
||||
- IMPORTANT: Only use ONE <dyad-write> block per file that you write!
|
||||
- Prioritize creating small, focused files and components.
|
||||
- do NOT be lazy and ALWAYS write the entire file. It needs to be a complete file.
|
||||
|
||||
Coding guidelines
|
||||
- ALWAYS generate responsive designs.
|
||||
- Use toasts components to inform the user about important events.
|
||||
- Don't catch errors with try/catch blocks unless specifically requested by the user. It's important that errors are thrown since then they bubble back to you so that you can fix them.
|
||||
|
||||
DO NOT OVERENGINEER THE CODE. You take great pride in keeping things simple and elegant. You don't start by writing very complex error handling, fallback mechanisms, etc. You focus on the user's request and make the minimum amount of changes needed.
|
||||
DON'T DO MORE THAN WHAT THE USER ASKS FOR.
|
||||
|
||||
# AI_RULES.md
|
||||
|
||||
|
||||
Directory names MUST be all lower-case (src/pages, src/components, etc.). File names may use mixed-case if you like.
|
||||
|
||||
# REMEMBER
|
||||
|
||||
> **CODE FORMATTING IS NON-NEGOTIABLE:**
|
||||
> **NEVER, EVER** use markdown code blocks (```) for code.
|
||||
> **ONLY** use <dyad-write> tags for **ALL** code output.
|
||||
> Using ``` for code is **PROHIBITED**.
|
||||
> Using <dyad-write> for code is **MANDATORY**.
|
||||
> Any instance of code within ``` is a **CRITICAL FAILURE**.
|
||||
> **REPEAT: NO MARKDOWN CODE BLOCKS. USE <dyad-write> EXCLUSIVELY FOR CODE.**
|
||||
> Do NOT use <dyad-file> tags in the output. ALWAYS use <dyad-write> to generate code.
|
||||
|
||||
|
||||
|
||||
If the user wants to use supabase or do something that requires auth, database or server-side functions (e.g. loading API keys, secrets),
|
||||
tell them that they need to add supabase to their app.
|
||||
|
||||
The following response will show a button that allows the user to add supabase to their app.
|
||||
|
||||
<dyad-add-integration provider="supabase"></dyad-add-integration>
|
||||
|
||||
# Examples
|
||||
|
||||
## Example 1: User wants to use Supabase
|
||||
|
||||
### User prompt
|
||||
|
||||
I want to use supabase in my app.
|
||||
|
||||
### Assistant response
|
||||
|
||||
You need to first add Supabase to your app.
|
||||
|
||||
<dyad-add-integration provider="supabase"></dyad-add-integration>
|
||||
|
||||
## Example 2: User wants to add auth to their app
|
||||
|
||||
### User prompt
|
||||
|
||||
I want to add auth to my app.
|
||||
|
||||
### Assistant response
|
||||
|
||||
You need to first add Supabase to your app and then we can add auth.
|
||||
|
||||
<dyad-add-integration provider="supabase"></dyad-add-integration>
|
||||
|
||||
|
||||
===
|
||||
role: user
|
||||
message: [dump]
|
||||
@@ -0,0 +1,14 @@
|
||||
- dialog:
|
||||
- heading "Codebase Context" [level=3]
|
||||
- paragraph:
|
||||
- text: Select the files to use as context.
|
||||
- img
|
||||
- textbox "src/**/*.tsx"
|
||||
- button "Add"
|
||||
- paragraph: Dyad will use the entire codebase as context.
|
||||
- heading "Exclude Paths" [level=3]
|
||||
- paragraph:
|
||||
- text: These files will be excluded from the context.
|
||||
- img
|
||||
- textbox "node_modules/**/*"
|
||||
- button "Add"
|
||||
@@ -0,0 +1,25 @@
|
||||
- dialog:
|
||||
- heading "Codebase Context" [level=3]
|
||||
- paragraph:
|
||||
- text: Select the files to use as context.
|
||||
- img
|
||||
- textbox "src/**/*.tsx"
|
||||
- button "Add"
|
||||
- text: /src\/\*\*\/\*\.ts 4 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- text: /manual\/\*\* 3 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- heading "Exclude Paths" [level=3]
|
||||
- paragraph:
|
||||
- text: These files will be excluded from the context.
|
||||
- img
|
||||
- textbox "node_modules/**/*"
|
||||
- button "Add"
|
||||
- text: /src\/components\/\*\* 2 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- text: manual/exclude/** 0 files, ~0 tokens
|
||||
- button:
|
||||
- img
|
||||
@@ -0,0 +1,25 @@
|
||||
- dialog:
|
||||
- heading "Codebase Context" [level=3]
|
||||
- paragraph:
|
||||
- text: Select the files to use as context.
|
||||
- img
|
||||
- textbox "src/**/*.tsx"
|
||||
- button "Add"
|
||||
- text: /src\/\*\*\/\*\.ts 4 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- text: /manual\/\*\* 3 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- heading "Exclude Paths" [level=3]
|
||||
- paragraph:
|
||||
- text: These files will be excluded from the context.
|
||||
- img
|
||||
- textbox "node_modules/**/*"
|
||||
- button "Add"
|
||||
- text: manual/exclude/** 0 files, ~0 tokens
|
||||
- button:
|
||||
- img
|
||||
- text: /src\/\*\* 7 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
@@ -0,0 +1,20 @@
|
||||
- dialog:
|
||||
- heading "Codebase Context" [level=3]
|
||||
- paragraph:
|
||||
- text: Select the files to use as context.
|
||||
- img
|
||||
- textbox "src/**/*.tsx"
|
||||
- button "Add"
|
||||
- paragraph: Dyad will use Smart Context to automatically find the most relevant files to use as context.
|
||||
- heading "Exclude Paths" [level=3]
|
||||
- paragraph:
|
||||
- text: These files will be excluded from the context.
|
||||
- img
|
||||
- textbox "node_modules/**/*"
|
||||
- button "Add"
|
||||
- heading "Smart Context Auto-includes" [level=3]
|
||||
- paragraph:
|
||||
- text: These files will always be included in the context.
|
||||
- img
|
||||
- textbox "src/**/*.config.ts"
|
||||
- button "Add"
|
||||
@@ -0,0 +1,37 @@
|
||||
- dialog:
|
||||
- heading "Codebase Context" [level=3]
|
||||
- paragraph:
|
||||
- text: Select the files to use as context.
|
||||
- img
|
||||
- textbox "src/**/*.tsx"
|
||||
- button "Add"
|
||||
- text: /src\/\*\*\/\*\.ts 4 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- text: /manual\/\*\* 3 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- heading "Exclude Paths" [level=3]
|
||||
- paragraph:
|
||||
- text: These files will be excluded from the context.
|
||||
- img
|
||||
- textbox "node_modules/**/*"
|
||||
- button "Add"
|
||||
- text: /src\/components\/\*\* 2 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- text: /exclude\/exclude\.ts 1 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- heading "Smart Context Auto-includes" [level=3]
|
||||
- paragraph:
|
||||
- text: These files will always be included in the context.
|
||||
- img
|
||||
- textbox "src/**/*.config.ts"
|
||||
- button "Add"
|
||||
- text: /a\.ts 1 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
- text: /exclude\/\*\* 2 files, ~\d+ tokens/
|
||||
- button:
|
||||
- img
|
||||
@@ -16,29 +16,33 @@ import {
|
||||
} from "./ui/tooltip";
|
||||
import { useSettings } from "@/hooks/useSettings";
|
||||
import { useContextPaths } from "@/hooks/useContextPaths";
|
||||
import type { ContextPathResult } from "@/lib/schemas";
|
||||
|
||||
export function ContextFilesPicker() {
|
||||
const { settings } = useSettings();
|
||||
const {
|
||||
contextPaths,
|
||||
smartContextAutoIncludes,
|
||||
excludePaths,
|
||||
updateContextPaths,
|
||||
updateSmartContextAutoIncludes,
|
||||
updateExcludePaths,
|
||||
} = useContextPaths();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [newPath, setNewPath] = useState("");
|
||||
const [newAutoIncludePath, setNewAutoIncludePath] = useState("");
|
||||
const [newExcludePath, setNewExcludePath] = useState("");
|
||||
|
||||
const addPath = () => {
|
||||
if (
|
||||
newPath.trim() === "" ||
|
||||
contextPaths.find((p) => p.globPath === newPath)
|
||||
contextPaths.find((p: ContextPathResult) => p.globPath === newPath)
|
||||
) {
|
||||
setNewPath("");
|
||||
return;
|
||||
}
|
||||
const newPaths = [
|
||||
...contextPaths.map(({ globPath }) => ({ globPath })),
|
||||
...contextPaths.map(({ globPath }: ContextPathResult) => ({ globPath })),
|
||||
{
|
||||
globPath: newPath,
|
||||
},
|
||||
@@ -49,21 +53,25 @@ export function ContextFilesPicker() {
|
||||
|
||||
const removePath = (pathToRemove: string) => {
|
||||
const newPaths = contextPaths
|
||||
.filter((p) => p.globPath !== pathToRemove)
|
||||
.map(({ globPath }) => ({ globPath }));
|
||||
.filter((p: ContextPathResult) => p.globPath !== pathToRemove)
|
||||
.map(({ globPath }: ContextPathResult) => ({ globPath }));
|
||||
updateContextPaths(newPaths);
|
||||
};
|
||||
|
||||
const addAutoIncludePath = () => {
|
||||
if (
|
||||
newAutoIncludePath.trim() === "" ||
|
||||
smartContextAutoIncludes.find((p) => p.globPath === newAutoIncludePath)
|
||||
smartContextAutoIncludes.find(
|
||||
(p: ContextPathResult) => p.globPath === newAutoIncludePath,
|
||||
)
|
||||
) {
|
||||
setNewAutoIncludePath("");
|
||||
return;
|
||||
}
|
||||
const newPaths = [
|
||||
...smartContextAutoIncludes.map(({ globPath }) => ({ globPath })),
|
||||
...smartContextAutoIncludes.map(({ globPath }: ContextPathResult) => ({
|
||||
globPath,
|
||||
})),
|
||||
{
|
||||
globPath: newAutoIncludePath,
|
||||
},
|
||||
@@ -74,11 +82,36 @@ export function ContextFilesPicker() {
|
||||
|
||||
const removeAutoIncludePath = (pathToRemove: string) => {
|
||||
const newPaths = smartContextAutoIncludes
|
||||
.filter((p) => p.globPath !== pathToRemove)
|
||||
.map(({ globPath }) => ({ globPath }));
|
||||
.filter((p: ContextPathResult) => p.globPath !== pathToRemove)
|
||||
.map(({ globPath }: ContextPathResult) => ({ globPath }));
|
||||
updateSmartContextAutoIncludes(newPaths);
|
||||
};
|
||||
|
||||
const addExcludePath = () => {
|
||||
if (
|
||||
newExcludePath.trim() === "" ||
|
||||
excludePaths.find((p: ContextPathResult) => p.globPath === newExcludePath)
|
||||
) {
|
||||
setNewExcludePath("");
|
||||
return;
|
||||
}
|
||||
const newPaths = [
|
||||
...excludePaths.map(({ globPath }: ContextPathResult) => ({ globPath })),
|
||||
{
|
||||
globPath: newExcludePath,
|
||||
},
|
||||
];
|
||||
updateExcludePaths(newPaths);
|
||||
setNewExcludePath("");
|
||||
};
|
||||
|
||||
const removeExcludePath = (pathToRemove: string) => {
|
||||
const newPaths = excludePaths
|
||||
.filter((p: ContextPathResult) => p.globPath !== pathToRemove)
|
||||
.map(({ globPath }: ContextPathResult) => ({ globPath }));
|
||||
updateExcludePaths(newPaths);
|
||||
};
|
||||
|
||||
const isSmartContextEnabled =
|
||||
settings?.enableDyadPro && settings?.enableProSmartFilesContextMode;
|
||||
|
||||
@@ -100,7 +133,10 @@ export function ContextFilesPicker() {
|
||||
<TooltipContent>Codebase Context</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<PopoverContent className="w-96" align="start">
|
||||
<PopoverContent
|
||||
className="w-96 max-h-[80vh] overflow-y-auto"
|
||||
align="start"
|
||||
>
|
||||
<div className="relative space-y-4">
|
||||
<div>
|
||||
<h3 className="font-medium">Codebase Context</h3>
|
||||
@@ -153,7 +189,7 @@ export function ContextFilesPicker() {
|
||||
<TooltipProvider>
|
||||
{contextPaths.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
{contextPaths.map((p) => (
|
||||
{contextPaths.map((p: ContextPathResult) => (
|
||||
<div
|
||||
key={p.globPath}
|
||||
className="flex items-center justify-between gap-2 rounded-md border p-2"
|
||||
@@ -197,6 +233,91 @@ export function ContextFilesPicker() {
|
||||
)}
|
||||
</TooltipProvider>
|
||||
|
||||
<div className="pt-2">
|
||||
<div>
|
||||
<h3 className="font-medium">Exclude Paths</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="flex items-center gap-1 cursor-help">
|
||||
These files will be excluded from the context.{" "}
|
||||
<InfoIcon className="ml-2 size-4" />
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-[300px]">
|
||||
<p>
|
||||
Exclude paths take precedence - files that match both
|
||||
include and exclude patterns will be excluded.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full max-w-sm items-center space-x-2 mt-4">
|
||||
<Input
|
||||
data-testid="exclude-context-files-input"
|
||||
type="text"
|
||||
placeholder="node_modules/**/*"
|
||||
value={newExcludePath}
|
||||
onChange={(e) => setNewExcludePath(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
addExcludePath();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
onClick={addExcludePath}
|
||||
data-testid="exclude-context-files-add-button"
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<TooltipProvider>
|
||||
{excludePaths.length > 0 && (
|
||||
<div className="space-y-2 mt-4">
|
||||
{excludePaths.map((p: ContextPathResult) => (
|
||||
<div
|
||||
key={p.globPath}
|
||||
className="flex items-center justify-between gap-2 rounded-md border p-2 border-red-200"
|
||||
>
|
||||
<div className="flex flex-1 flex-col overflow-hidden">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="truncate font-mono text-sm text-red-600">
|
||||
{p.globPath}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{p.globPath}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{p.files} files, ~{p.tokens} tokens
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => removeExcludePath(p.globPath)}
|
||||
data-testid="exclude-context-files-remove-button"
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
|
||||
{isSmartContextEnabled && (
|
||||
<div className="pt-2">
|
||||
<div>
|
||||
@@ -247,7 +368,7 @@ export function ContextFilesPicker() {
|
||||
<TooltipProvider>
|
||||
{smartContextAutoIncludes.length > 0 && (
|
||||
<div className="space-y-2 mt-4">
|
||||
{smartContextAutoIncludes.map((p) => (
|
||||
{smartContextAutoIncludes.map((p: ContextPathResult) => (
|
||||
<div
|
||||
key={p.globPath}
|
||||
className="flex items-center justify-between gap-2 rounded-md border p-2"
|
||||
|
||||
@@ -15,7 +15,12 @@ export function useContextPaths() {
|
||||
} = useQuery<ContextPathResults, Error>({
|
||||
queryKey: ["context-paths", appId],
|
||||
queryFn: async () => {
|
||||
if (!appId) return { contextPaths: [], smartContextAutoIncludes: [] };
|
||||
if (!appId)
|
||||
return {
|
||||
contextPaths: [],
|
||||
smartContextAutoIncludes: [],
|
||||
excludePaths: [],
|
||||
};
|
||||
const ipcClient = IpcClient.getInstance();
|
||||
return ipcClient.getChatContextResults({ appId });
|
||||
},
|
||||
@@ -25,9 +30,17 @@ export function useContextPaths() {
|
||||
const updateContextPathsMutation = useMutation<
|
||||
unknown,
|
||||
Error,
|
||||
{ contextPaths: GlobPath[]; smartContextAutoIncludes?: GlobPath[] }
|
||||
{
|
||||
contextPaths: GlobPath[];
|
||||
smartContextAutoIncludes?: GlobPath[];
|
||||
excludePaths?: GlobPath[];
|
||||
}
|
||||
>({
|
||||
mutationFn: async ({ contextPaths, smartContextAutoIncludes }) => {
|
||||
mutationFn: async ({
|
||||
contextPaths,
|
||||
smartContextAutoIncludes,
|
||||
excludePaths,
|
||||
}) => {
|
||||
if (!appId) throw new Error("No app selected");
|
||||
const ipcClient = IpcClient.getInstance();
|
||||
return ipcClient.setChatContext({
|
||||
@@ -35,6 +48,7 @@ export function useContextPaths() {
|
||||
chatContext: {
|
||||
contextPaths,
|
||||
smartContextAutoIncludes: smartContextAutoIncludes || [],
|
||||
excludePaths: excludePaths || [],
|
||||
},
|
||||
});
|
||||
},
|
||||
@@ -46,28 +60,63 @@ export function useContextPaths() {
|
||||
const updateContextPaths = async (paths: GlobPath[]) => {
|
||||
const currentAutoIncludes =
|
||||
contextPathsData?.smartContextAutoIncludes || [];
|
||||
const currentExcludePaths = contextPathsData?.excludePaths || [];
|
||||
return updateContextPathsMutation.mutateAsync({
|
||||
contextPaths: paths,
|
||||
smartContextAutoIncludes: currentAutoIncludes.map(({ globPath }) => ({
|
||||
globPath,
|
||||
})),
|
||||
smartContextAutoIncludes: currentAutoIncludes.map(
|
||||
({ globPath }: { globPath: string }) => ({
|
||||
globPath,
|
||||
}),
|
||||
),
|
||||
excludePaths: currentExcludePaths.map(
|
||||
({ globPath }: { globPath: string }) => ({
|
||||
globPath,
|
||||
}),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const updateSmartContextAutoIncludes = async (paths: GlobPath[]) => {
|
||||
const currentContextPaths = contextPathsData?.contextPaths || [];
|
||||
const currentExcludePaths = contextPathsData?.excludePaths || [];
|
||||
return updateContextPathsMutation.mutateAsync({
|
||||
contextPaths: currentContextPaths.map(({ globPath }) => ({ globPath })),
|
||||
contextPaths: currentContextPaths.map(
|
||||
({ globPath }: { globPath: string }) => ({ globPath }),
|
||||
),
|
||||
smartContextAutoIncludes: paths,
|
||||
excludePaths: currentExcludePaths.map(
|
||||
({ globPath }: { globPath: string }) => ({
|
||||
globPath,
|
||||
}),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const updateExcludePaths = async (paths: GlobPath[]) => {
|
||||
const currentContextPaths = contextPathsData?.contextPaths || [];
|
||||
const currentAutoIncludes =
|
||||
contextPathsData?.smartContextAutoIncludes || [];
|
||||
return updateContextPathsMutation.mutateAsync({
|
||||
contextPaths: currentContextPaths.map(
|
||||
({ globPath }: { globPath: string }) => ({ globPath }),
|
||||
),
|
||||
smartContextAutoIncludes: currentAutoIncludes.map(
|
||||
({ globPath }: { globPath: string }) => ({
|
||||
globPath,
|
||||
}),
|
||||
),
|
||||
excludePaths: paths,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
contextPaths: contextPathsData?.contextPaths || [],
|
||||
smartContextAutoIncludes: contextPathsData?.smartContextAutoIncludes || [],
|
||||
excludePaths: contextPathsData?.excludePaths || [],
|
||||
isLoading,
|
||||
error,
|
||||
updateContextPaths,
|
||||
updateSmartContextAutoIncludes,
|
||||
updateExcludePaths,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,10 +39,10 @@ export function registerContextPathsHandlers() {
|
||||
const results: ContextPathResults = {
|
||||
contextPaths: [],
|
||||
smartContextAutoIncludes: [],
|
||||
excludePaths: [],
|
||||
};
|
||||
const { contextPaths, smartContextAutoIncludes } = validateChatContext(
|
||||
app.chatContext,
|
||||
);
|
||||
const { contextPaths, smartContextAutoIncludes, excludePaths } =
|
||||
validateChatContext(app.chatContext);
|
||||
for (const contextPath of contextPaths) {
|
||||
const { formattedOutput, files } = await extractCodebase({
|
||||
appPath,
|
||||
@@ -76,6 +76,23 @@ export function registerContextPathsHandlers() {
|
||||
tokens: totalTokens,
|
||||
});
|
||||
}
|
||||
|
||||
for (const excludePath of excludePaths || []) {
|
||||
const { formattedOutput, files } = await extractCodebase({
|
||||
appPath,
|
||||
chatContext: {
|
||||
contextPaths: [excludePath],
|
||||
smartContextAutoIncludes: [],
|
||||
},
|
||||
});
|
||||
const totalTokens = estimateTokens(formattedOutput);
|
||||
|
||||
results.excludePaths.push({
|
||||
...excludePath,
|
||||
files: files.length,
|
||||
tokens: totalTokens,
|
||||
});
|
||||
}
|
||||
return results;
|
||||
},
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ export function validateChatContext(chatContext: unknown): AppChatContext {
|
||||
return {
|
||||
contextPaths: [],
|
||||
smartContextAutoIncludes: [],
|
||||
excludePaths: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,6 +21,7 @@ export function validateChatContext(chatContext: unknown): AppChatContext {
|
||||
return {
|
||||
contextPaths: [],
|
||||
smartContextAutoIncludes: [],
|
||||
excludePaths: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ export type GlobPath = z.infer<typeof GlobPathSchema>;
|
||||
export const AppChatContextSchema = z.object({
|
||||
contextPaths: z.array(GlobPathSchema),
|
||||
smartContextAutoIncludes: z.array(GlobPathSchema),
|
||||
excludePaths: z.array(GlobPathSchema).optional(),
|
||||
});
|
||||
export type AppChatContext = z.infer<typeof AppChatContextSchema>;
|
||||
|
||||
@@ -131,6 +132,7 @@ export type ContextPathResult = GlobPath & {
|
||||
export type ContextPathResults = {
|
||||
contextPaths: ContextPathResult[];
|
||||
smartContextAutoIncludes: ContextPathResult[];
|
||||
excludePaths: ContextPathResult[];
|
||||
};
|
||||
|
||||
export const ReleaseChannelSchema = z.enum(["stable", "beta"]);
|
||||
|
||||
@@ -472,9 +472,10 @@ export async function extractCodebase({
|
||||
}
|
||||
|
||||
// Collect files from contextPaths and smartContextAutoIncludes
|
||||
const { contextPaths, smartContextAutoIncludes } = chatContext;
|
||||
const { contextPaths, smartContextAutoIncludes, excludePaths } = chatContext;
|
||||
const includedFiles = new Set<string>();
|
||||
const autoIncludedFiles = new Set<string>();
|
||||
const excludedFiles = new Set<string>();
|
||||
|
||||
// Add files from contextPaths
|
||||
if (contextPaths && contextPaths.length > 0) {
|
||||
@@ -509,6 +510,7 @@ export async function extractCodebase({
|
||||
const matches = await glob(pattern, {
|
||||
nodir: true,
|
||||
absolute: true,
|
||||
ignore: "**/node_modules/**",
|
||||
});
|
||||
matches.forEach((file) => {
|
||||
const normalizedFile = path.normalize(file);
|
||||
@@ -518,12 +520,36 @@ export async function extractCodebase({
|
||||
}
|
||||
}
|
||||
|
||||
// Add files from excludePaths
|
||||
if (excludePaths && excludePaths.length > 0) {
|
||||
for (const p of excludePaths) {
|
||||
const pattern = createFullGlobPath({
|
||||
appPath,
|
||||
globPath: p.globPath,
|
||||
});
|
||||
const matches = await glob(pattern, {
|
||||
nodir: true,
|
||||
absolute: true,
|
||||
ignore: "**/node_modules/**",
|
||||
});
|
||||
matches.forEach((file) => {
|
||||
const normalizedFile = path.normalize(file);
|
||||
excludedFiles.add(normalizedFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Only filter files if contextPaths are provided
|
||||
// If only smartContextAutoIncludes are provided, keep all files and just mark auto-includes as forced
|
||||
if (contextPaths && contextPaths.length > 0) {
|
||||
files = files.filter((file) => includedFiles.has(path.normalize(file)));
|
||||
}
|
||||
|
||||
// Filter out excluded files (this takes precedence over include paths)
|
||||
if (excludedFiles.size > 0) {
|
||||
files = files.filter((file) => !excludedFiles.has(path.normalize(file)));
|
||||
}
|
||||
|
||||
// Sort files by modification time (oldest first)
|
||||
// This is important for cache-ability.
|
||||
const sortedFiles = await sortFilesByModificationTime([...new Set(files)]);
|
||||
@@ -543,7 +569,9 @@ export async function extractCodebase({
|
||||
virtualFileSystem,
|
||||
});
|
||||
|
||||
const isForced = autoIncludedFiles.has(path.normalize(file));
|
||||
const isForced =
|
||||
autoIncludedFiles.has(path.normalize(file)) &&
|
||||
!excludedFiles.has(path.normalize(file));
|
||||
|
||||
// Determine file content based on whether we should read it
|
||||
let fileContent: string;
|
||||
|
||||
Reference in New Issue
Block a user