Skip to content

Commit 407fd26

Browse files
authored
feat: add agent rules to context (#2946)
1 parent 96de25e commit 407fd26

File tree

7 files changed

+71
-51
lines changed

7 files changed

+71
-51
lines changed

apps/web/client/public/onlook-preload-script.js

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/context-pills/helpers.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ export function getContextIcon(context: MessageContext) {
3333
return (
3434
<NodeIcon tagName={context.displayName} iconClass="w-3 h-3 ml-1 mr-2 flex-none" />
3535
);
36-
case MessageContextType.PROJECT:
37-
icon = Icons.Cube;
38-
break;
3936
case MessageContextType.BRANCH:
4037
icon = Icons.Branch;
4138
break;
39+
case MessageContextType.AGENT_RULE:
40+
icon = Icons.Cube;
41+
break;
4242
default:
4343
assertNever(context);
4444
}

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/context-pills/input-context-pills.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import { DraftImagePill } from './draft-image-pill';
99

1010
const typeOrder = {
1111
[MessageContextType.BRANCH]: 0,
12-
[MessageContextType.PROJECT]: 1,
13-
[MessageContextType.FILE]: 2,
14-
[MessageContextType.HIGHLIGHT]: 3,
15-
[MessageContextType.ERROR]: 4,
16-
[MessageContextType.IMAGE]: 5,
12+
[MessageContextType.FILE]: 1,
13+
[MessageContextType.HIGHLIGHT]: 2,
14+
[MessageContextType.ERROR]: 3,
15+
[MessageContextType.IMAGE]: 4,
16+
[MessageContextType.AGENT_RULE]: 5,
1717
};
1818

1919
export const InputContextPills = observer(() => {

apps/web/client/src/components/store/editor/chat/context.ts

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { ChatType, type DomElement } from '@onlook/models';
22
import {
33
MessageContextType,
4+
type AgentRuleMessageContext,
45
type BranchMessageContext,
56
type ErrorMessageContext,
67
type FileMessageContext,
78
type HighlightMessageContext,
89
type ImageMessageContext,
9-
type MessageContext,
10-
type ProjectMessageContext,
10+
type MessageContext
1111
} from '@onlook/models/chat';
1212
import { assertNever, type ParsedError } from '@onlook/utility';
1313
import { makeAutoObservable, reaction } from 'mobx';
@@ -51,16 +51,16 @@ export class ChatContext {
5151
case ChatType.EDIT:
5252
case ChatType.CREATE:
5353
case ChatType.ASK:
54-
return await this.getLatestContext();
54+
return await this.getChatEditContext();
5555
case ChatType.FIX:
5656
return this.getErrorContext();
5757
default:
5858
assertNever(type);
5959
}
6060
}
6161

62-
async getLatestContext(): Promise<MessageContext[]> {
63-
return await this.getRefreshedContext(this.context);
62+
async getChatEditContext(): Promise<MessageContext[]> {
63+
return [...await this.getRefreshedContext(this.context), ...await this.getAgentRuleContext()];
6464
}
6565

6666
private async generateContextFromReaction({ elements, frames }: { elements: DomElement[], frames: FrameData[] }): Promise<MessageContext[]> {
@@ -215,16 +215,34 @@ export class ChatContext {
215215
};
216216
}
217217

218-
// TODO: Enhance with custom rules
219-
getProjectContext(): ProjectMessageContext[] {
220-
return [
221-
{
222-
type: MessageContextType.PROJECT,
223-
content: '',
224-
displayName: 'Project',
225-
path: './',
226-
},
227-
];
218+
private async getAgentRuleContext(): Promise<AgentRuleMessageContext[]> {
219+
try {
220+
const agentRuleFilesPaths = ['agents.md', 'claude.md', 'AGENTS.md', 'CLAUDE.md'];
221+
const agentRuleContexts: AgentRuleMessageContext[] = (await Promise.all(
222+
agentRuleFilesPaths.map(async (filePath) => {
223+
const file = await this.editorEngine.activeSandbox.readFile(`./${filePath}`);
224+
if (file === null) {
225+
return null;
226+
}
227+
if (file.type === 'binary') {
228+
return null;
229+
}
230+
if (file.content.trim().length === 0) {
231+
return null;
232+
}
233+
return {
234+
type: MessageContextType.AGENT_RULE,
235+
content: file.content,
236+
displayName: filePath,
237+
path: filePath,
238+
} satisfies AgentRuleMessageContext;
239+
})
240+
)).filter((context) => context !== null);
241+
return agentRuleContexts
242+
} catch (error) {
243+
console.error('Error getting agent rule context', error);
244+
return [];
245+
}
228246
}
229247

230248
getErrorContext(): ErrorMessageContext[] {

packages/ai/src/prompt/constants/context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ IMPORTANT: This project uses Bun as the package manager. Always use Bun commands
2121
2222
NEVER SUGGEST THE "bun run dev" command. Assume the user is already running the app.`;
2323

24-
const projectContextPrefix = `This is a Nextjs project with TailwindCSS`;
24+
const agentRulesContextPrefix = `These are user provided rules for the project`;
2525

2626
export const CONTEXT_PROMPTS = {
2727
filesContentPrefix,
2828
truncatedFilesContentPrefix,
2929
highlightPrefix,
3030
errorsContentPrefix,
31-
projectContextPrefix,
31+
agentRulesContextPrefix,
3232
};

packages/ai/src/prompt/provider.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {
22
MessageContextType,
3+
type AgentRuleMessageContext,
34
type BranchMessageContext,
45
type ChatMessage,
56
type ErrorMessageContext,
67
type FileMessageContext,
78
type HighlightMessageContext,
8-
type MessageContext,
9-
type ProjectMessageContext,
9+
type MessageContext
1010
} from '@onlook/models';
1111
import type { FileUIPart } from 'ai';
1212
import { ASK_MODE_SYSTEM_PROMPT, CODE_FENCE, CONTEXT_PROMPTS, CREATE_NEW_PAGE_SYSTEM_PROMPT, SHELL_PROMPT, SUGGESTION_SYSTEM_PROMPT, SUMMARY_PROMPTS, SYSTEM_PROMPT } from './constants';
@@ -67,7 +67,7 @@ export function getHydratedUserMessage(
6767
const files = context.filter((c) => c.type === MessageContextType.FILE).map((c) => c);
6868
const highlights = context.filter((c) => c.type === MessageContextType.HIGHLIGHT).map((c) => c);
6969
const errors = context.filter((c) => c.type === MessageContextType.ERROR).map((c) => c);
70-
const project = context.filter((c) => c.type === MessageContextType.PROJECT).map((c) => c);
70+
const agentRules = context.filter((c) => c.type === MessageContextType.AGENT_RULE).map((c) => c);
7171
const images = context.filter((c) => c.type === MessageContextType.IMAGE).map((c) => c);
7272
const branches = context.filter((c) => c.type === MessageContextType.BRANCH).map((c) => c);
7373

@@ -94,11 +94,9 @@ export function getHydratedUserMessage(
9494
prompt += errorPrompt;
9595
}
9696

97-
if (project.length > 0) {
98-
const projectContext = project[0];
99-
if (projectContext) {
100-
prompt += getProjectContext(projectContext);
101-
}
97+
if (agentRules.length > 0) {
98+
const agentRulePrompt = getAgentRulesContent(agentRules);
99+
prompt += agentRulePrompt;
102100
}
103101

104102
if (branches.length > 0) {
@@ -242,7 +240,11 @@ export function getSummaryPrompt() {
242240
return prompt;
243241
}
244242

245-
export function getProjectContext(project: ProjectMessageContext) {
246-
const content = `${CONTEXT_PROMPTS.projectContextPrefix} ${project.path}`;
247-
return wrapXml('project-info', content);
243+
export function getAgentRulesContent(agentRules: AgentRuleMessageContext[]) {
244+
let content = `${CONTEXT_PROMPTS.agentRulesContextPrefix}\n`;
245+
for (const agentRule of agentRules) {
246+
const agentRulePrompt = `${agentRule.path}\n${agentRule.content}`;
247+
content += agentRulePrompt;
248+
}
249+
return wrapXml('agent-rules', content);
248250
}

packages/models/src/chat/message/context.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export enum MessageContextType {
55
HIGHLIGHT = 'highlight',
66
IMAGE = 'image',
77
ERROR = 'error',
8-
PROJECT = 'project',
98
BRANCH = 'branch',
9+
AGENT_RULE = 'agent_rule',
1010
}
1111

1212
type BaseMessageContext = {
@@ -45,15 +45,15 @@ export type ErrorMessageContext = BaseMessageContext & {
4545
branchId: string;
4646
};
4747

48-
export type ProjectMessageContext = BaseMessageContext & {
49-
type: MessageContextType.PROJECT;
48+
export type AgentRuleMessageContext = BaseMessageContext & {
49+
type: MessageContextType.AGENT_RULE;
5050
path: string;
5151
};
5252

5353
export type MessageContext =
5454
| HighlightMessageContext
5555
| ImageMessageContext
5656
| ErrorMessageContext
57-
| ProjectMessageContext
57+
| AgentRuleMessageContext
5858
| BranchMessageContext
5959
| FileMessageContext;

0 commit comments

Comments
 (0)