big format

This commit is contained in:
Dax Raad
2025-11-06 13:03:02 -05:00
parent 8729edc5e0
commit 1ea3a8eb9b
183 changed files with 2629 additions and 2497 deletions

View File

@@ -23,8 +23,13 @@ export const EditTool = Tool.define("edit", {
parameters: z.object({
filePath: z.string().describe("The absolute path to the file to modify"),
oldString: z.string().describe("The text to replace"),
newString: z.string().describe("The text to replace it with (must be different from oldString)"),
replaceAll: z.boolean().optional().describe("Replace all occurrences of oldString (default false)"),
newString: z
.string()
.describe("The text to replace it with (must be different from oldString)"),
replaceAll: z
.boolean()
.optional()
.describe("Replace all occurrences of oldString (default false)"),
}),
async execute(params, ctx) {
if (!params.filePath) {
@@ -35,7 +40,9 @@ export const EditTool = Tool.define("edit", {
throw new Error("oldString and newString must be different")
}
const filePath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath)
const filePath = path.isAbsolute(params.filePath)
? params.filePath
: path.join(Instance.directory, params.filePath)
if (!Filesystem.contains(Instance.directory, filePath)) {
const parentDir = path.dirname(filePath)
await Permission.ask({
@@ -172,7 +179,11 @@ function levenshtein(a: string, b: string): number {
for (let i = 1; i <= a.length; i++) {
for (let j = 1; j <= b.length; j++) {
const cost = a[i - 1] === b[j - 1] ? 0 : 1
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost)
matrix[i][j] = Math.min(
matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + cost,
)
}
}
return matrix[a.length][b.length]
@@ -374,7 +385,9 @@ export const WhitespaceNormalizedReplacer: Replacer = function* (content, find)
// Find the actual substring in the original line that matches
const words = find.trim().split(/\s+/)
if (words.length > 0) {
const pattern = words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+")
const pattern = words
.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
.join("\\s+")
try {
const regex = new RegExp(pattern)
const match = line.match(regex)
@@ -612,7 +625,12 @@ export function trimDiff(diff: string): string {
return trimmedLines.join("\n")
}
export function replace(content: string, oldString: string, newString: string, replaceAll = false): string {
export function replace(
content: string,
oldString: string,
newString: string,
replaceAll = false,
): string {
if (oldString === newString) {
throw new Error("oldString and newString must be different")
}

View File

@@ -9,8 +9,14 @@ export const GrepTool = Tool.define("grep", {
description: DESCRIPTION,
parameters: z.object({
pattern: z.string().describe("The regex pattern to search for in file contents"),
path: z.string().optional().describe("The directory to search in. Defaults to the current working directory."),
include: z.string().optional().describe('File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")'),
path: z
.string()
.optional()
.describe("The directory to search in. Defaults to the current working directory."),
include: z
.string()
.optional()
.describe('File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")'),
}),
async execute(params) {
if (!params.pattern) {

View File

@@ -37,13 +37,18 @@ const LIMIT = 100
export const ListTool = Tool.define("list", {
description: DESCRIPTION,
parameters: z.object({
path: z.string().describe("The absolute path to the directory to list (must be absolute, not relative)").optional(),
path: z
.string()
.describe("The absolute path to the directory to list (must be absolute, not relative)")
.optional(),
ignore: z.array(z.string()).describe("List of glob patterns to ignore").optional(),
}),
async execute(params) {
const searchPath = path.resolve(Instance.directory, params.path || ".")
const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat(params.ignore?.map((p) => `!${p}`) || [])
const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat(
params.ignore?.map((p) => `!${p}`) || [],
)
const files = []
for await (const file of Ripgrep.files({ cwd: searchPath, glob: ignoreGlobs })) {
files.push(file)

View File

@@ -11,7 +11,9 @@ export const LspDiagnosticTool = Tool.define("lsp_diagnostics", {
path: z.string().describe("The path to the file to get diagnostics."),
}),
execute: async (args) => {
const normalized = path.isAbsolute(args.path) ? args.path : path.join(Instance.directory, args.path)
const normalized = path.isAbsolute(args.path)
? args.path
: path.join(Instance.directory, args.path)
await LSP.touchFile(normalized, true)
const diagnostics = await LSP.diagnostics()
const file = diagnostics[normalized]

View File

@@ -14,8 +14,13 @@ export const MultiEditTool = Tool.define("multiedit", {
z.object({
filePath: z.string().describe("The absolute path to the file to modify"),
oldString: z.string().describe("The text to replace"),
newString: z.string().describe("The text to replace it with (must be different from oldString)"),
replaceAll: z.boolean().optional().describe("Replace all occurrences of oldString (default false)"),
newString: z
.string()
.describe("The text to replace it with (must be different from oldString)"),
replaceAll: z
.boolean()
.optional()
.describe("Replace all occurrences of oldString (default false)"),
}),
)
.describe("Array of edit operations to perform sequentially on the file"),

View File

@@ -18,7 +18,10 @@ export const ReadTool = Tool.define("read", {
description: DESCRIPTION,
parameters: z.object({
filePath: z.string().describe("The path to the file to read"),
offset: z.coerce.number().describe("The line number to start reading from (0-based)").optional(),
offset: z.coerce
.number()
.describe("The line number to start reading from (0-based)")
.optional(),
limit: z.coerce.number().describe("The number of lines to read (defaults to 2000)").optional(),
}),
async execute(params, ctx) {
@@ -53,13 +56,16 @@ export const ReadTool = Tool.define("read", {
const suggestions = dirEntries
.filter(
(entry) =>
entry.toLowerCase().includes(base.toLowerCase()) || base.toLowerCase().includes(entry.toLowerCase()),
entry.toLowerCase().includes(base.toLowerCase()) ||
base.toLowerCase().includes(entry.toLowerCase()),
)
.map((entry) => path.join(dir, entry))
.slice(0, 3)
if (suggestions.length > 0) {
throw new Error(`File not found: ${filepath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`)
throw new Error(
`File not found: ${filepath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`,
)
}
throw new Error(`File not found: ${filepath}`)

View File

@@ -24,7 +24,12 @@ export namespace ToolRegistry {
const glob = new Bun.Glob("tool/*.{js,ts}")
for (const dir of await Config.directories()) {
for await (const match of glob.scan({ cwd: dir, absolute: true, followSymlinks: true, dot: true })) {
for await (const match of glob.scan({
cwd: dir,
absolute: true,
followSymlinks: true,
dot: true,
})) {
const namespace = path.basename(match, path.extname(match))
const mod = await import(match)
for (const [id, def] of Object.entries<ToolDefinition>(mod)) {

View File

@@ -14,7 +14,10 @@ export const TaskTool = Tool.define("task", async () => {
const description = DESCRIPTION.replace(
"{agents}",
agents
.map((a) => `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`)
.map(
(a) =>
`- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`,
)
.join("\n"),
)
return {
@@ -26,7 +29,8 @@ export const TaskTool = Tool.define("task", async () => {
}),
async execute(params, ctx) {
const agent = await Agent.get(params.subagent_type)
if (!agent) throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`)
if (!agent)
throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`)
const session = await Session.create({
parentID: ctx.sessionID,
title: params.description + ` (@${agent.name} subagent)`,
@@ -91,7 +95,9 @@ export const TaskTool = Tool.define("task", async () => {
let all
all = await Session.messages(session.id)
all = all.filter((x) => x.info.role === "assistant")
all = all.flatMap((msg) => msg.parts.filter((x: any) => x.type === "tool") as MessageV2.ToolPart[])
all = all.flatMap(
(msg) => msg.parts.filter((x: any) => x.type === "tool") as MessageV2.ToolPart[],
)
return {
title: params.description,
metadata: {

View File

@@ -48,13 +48,15 @@ export const WebFetchTool = Tool.define("webfetch", {
let acceptHeader = "*/*"
switch (params.format) {
case "markdown":
acceptHeader = "text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1"
acceptHeader =
"text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1"
break
case "text":
acceptHeader = "text/plain;q=1.0, text/markdown;q=0.9, text/html;q=0.8, */*;q=0.1"
break
case "html":
acceptHeader = "text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1"
acceptHeader =
"text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1"
break
default:
acceptHeader =
@@ -158,7 +160,9 @@ async function extractTextFromHTML(html: string) {
.on("*", {
element(element) {
// Reset skip flag when entering other elements
if (!["script", "style", "noscript", "iframe", "object", "embed"].includes(element.tagName)) {
if (
!["script", "style", "noscript", "iframe", "object", "embed"].includes(element.tagName)
) {
skipContent = false
}
},

View File

@@ -15,10 +15,14 @@ export const WriteTool = Tool.define("write", {
description: DESCRIPTION,
parameters: z.object({
content: z.string().describe("The content to write to the file"),
filePath: z.string().describe("The absolute path to the file to write (must be absolute, not relative)"),
filePath: z
.string()
.describe("The absolute path to the file to write (must be absolute, not relative)"),
}),
async execute(params, ctx) {
const filepath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath)
const filepath = path.isAbsolute(params.filePath)
? params.filePath
: path.join(Instance.directory, params.filePath)
if (!Filesystem.contains(Instance.directory, filepath)) {
const parentDir = path.dirname(filepath)
await Permission.ask({