forked from onlook-dev/onlook
-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathindex.ts
More file actions
156 lines (148 loc) · 5.76 KB
/
index.ts
File metadata and controls
156 lines (148 loc) · 5.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import { anthropic } from '@ai-sdk/anthropic';
import { tool, type ToolSet } from 'ai';
import { readFile } from 'fs/promises';
import { z } from 'zod';
import { ONLOOK_PROMPT } from '../prompt/onlook';
import { getAllFiles, getBrandConfigFiles } from './helpers';
export const listFilesTool = tool({
description: 'List all files in the current directory, including subdirectories',
parameters: z.object({
path: z
.string()
.describe(
'The absolute path to the directory to get files from. This should be the root directory of the project.',
),
}),
execute: async ({ path }) => {
const res = await getAllFiles(path);
if (!res.success) {
return { error: res.error };
}
return res.files;
},
});
export const readFilesTool = tool({
description: 'Read the contents of files',
parameters: z.object({
paths: z.array(z.string()).describe('The absolute paths to the files to read'),
}),
execute: async ({ paths }) => {
try {
const files = await Promise.all(
paths.map(async (path) => {
const file = await readFile(path, 'utf8');
return { path, content: file };
}),
);
return files;
} catch (error) {
return `Error: ${error instanceof Error ? error.message : error}`;
}
},
});
export const onlookInstructionsTool = tool({
description: 'Get the instructions for the Onlook AI',
parameters: z.object({}),
execute: async () => {
return ONLOOK_PROMPT;
},
});
// https://docs.anthropic.com/en/docs/agents-and-tools/computer-use#understand-anthropic-defined-tools
// https://sdk.vercel.ai/docs/guides/computer-use#get-started-with-computer-use
// We currently can't use this because it doens't support streaming
interface FileOperationHandlers {
readFile: (path: string) => Promise<string>;
writeFile: (path: string, content: string) => Promise<boolean>;
undoEdit?: () => Promise<boolean>;
}
export const getStrReplaceEditorTool = (handlers: FileOperationHandlers) => {
const strReplaceEditorTool = anthropic.tools.textEditor_20250124({
execute: async ({
command,
path,
file_text,
insert_line,
new_str,
old_str,
view_range,
}) => {
try {
switch (command) {
case 'view': {
const content = await handlers.readFile(path);
if (view_range) {
const lines = content.split('\n');
const [start, end] = view_range;
return lines.slice(start - 1, end).join('\n');
}
return content;
}
case 'create': {
if (!file_text) {
throw new Error('file_text is required for create command');
}
await handlers.writeFile(path, file_text);
return `File created successfully at ${path}`;
}
case 'str_replace': {
if (!old_str) {
throw new Error('old_str is required for str_replace command');
}
const content = await handlers.readFile(path);
const newContent = content.replace(old_str, new_str || '');
await handlers.writeFile(path, newContent);
return `String replaced successfully in ${path}`;
}
case 'insert': {
if (!new_str || insert_line === undefined) {
throw new Error(
'new_str and insert_line are required for insert command',
);
}
const content = await handlers.readFile(path);
const lines = content.split('\n');
lines.splice(insert_line, 0, new_str);
await handlers.writeFile(path, lines.join('\n'));
return `Content inserted successfully at line ${insert_line} in ${path}`;
}
case 'undo_edit': {
if (handlers.undoEdit) {
await handlers.undoEdit();
return 'Edit undone successfully';
}
return 'Undo operation not implemented';
}
default: {
throw new Error(`Unknown command: ${command}`);
}
}
} catch (error) {
return `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
}
},
});
return strReplaceEditorTool;
};
export const getBrandConfigTool = tool({
description: 'Get the brand config of the current project',
parameters: z.object({
path: z
.string()
.describe(
'The absolute path to the directory to get files from. This should be the root directory of the project.',
),
}),
execute: async ({ path }) => {
const res = await getBrandConfigFiles(path);
if (!res.success) {
return { error: res.error };
}
return res.files;
},
});
export const chatToolSet: ToolSet = {
list_files: listFilesTool,
read_files: readFilesTool,
onlook_instructions: onlookInstructionsTool,
get_brand_config: getBrandConfigTool,
};