fix tests

This commit is contained in:
Dax Raad 2025-10-31 13:27:58 -04:00
parent 9460dfaf4c
commit d102e888cb
8 changed files with 83 additions and 83 deletions

View file

@ -207,10 +207,8 @@
"partial-json": "0.1.7",
"remeda": "catalog:",
"solid-js": "catalog:",
"tree-sitter": "0.22.4",
"tree-sitter-bash": "0.23.3",
"tree-sitter-highlight": "1.0.1",
"tree-sitter-typescript": "0.23.2",
"tree-sitter": "0.25.0",
"tree-sitter-bash": "0.25.0",
"turndown": "7.2.0",
"ulid": "catalog:",
"vscode-jsonrpc": "8.2.1",
@ -3308,15 +3306,9 @@
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"tree-sitter": ["tree-sitter@0.22.4", "", { "dependencies": { "node-addon-api": "^8.3.0", "node-gyp-build": "^4.8.4" } }, "sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg=="],
"tree-sitter": ["tree-sitter@0.25.0", "", { "dependencies": { "node-addon-api": "^8.3.0", "node-gyp-build": "^4.8.4" } }, "sha512-PGZZzFW63eElZJDe/b/R/LbsjDDYJa5UEjLZJB59RQsMX+fo0j54fqBPn1MGKav/QNa0JR0zBiVaikYDWCj5KQ=="],
"tree-sitter-bash": ["tree-sitter-bash@0.23.3", "", { "dependencies": { "node-addon-api": "^8.2.1", "node-gyp-build": "^4.8.2" }, "peerDependencies": { "tree-sitter": "^0.21.1" }, "optionalPeers": ["tree-sitter"] }, "sha512-36cg/GQ2YmIbeiBeqeuh4fBJ6i4kgVouDaqTxqih5ysPag+zHufyIaxMOFeM8CeplwAK/Luj1o5XHqgdAfoCZg=="],
"tree-sitter-highlight": ["tree-sitter-highlight@1.0.1", "", {}, "sha512-PKoUNRI++rVXLsiYP5s3Ro5TnamZ0+7GMIzTGiga0i4YsyWlleei9cdBjX1GWCSAreOw+fnhqCQF5bHChnY8KQ=="],
"tree-sitter-javascript": ["tree-sitter-javascript@0.23.1", "", { "dependencies": { "node-addon-api": "^8.2.2", "node-gyp-build": "^4.8.2" }, "peerDependencies": { "tree-sitter": "^0.21.1" }, "optionalPeers": ["tree-sitter"] }, "sha512-/bnhbrTD9frUYHQTiYnPcxyHORIw157ERBa6dqzaKxvR/x3PC4Yzd+D1pZIMS6zNg2v3a8BZ0oK7jHqsQo9fWA=="],
"tree-sitter-typescript": ["tree-sitter-typescript@0.23.2", "", { "dependencies": { "node-addon-api": "^8.2.2", "node-gyp-build": "^4.8.2", "tree-sitter-javascript": "^0.23.1" }, "peerDependencies": { "tree-sitter": "^0.21.0" }, "optionalPeers": ["tree-sitter"] }, "sha512-e04JUUKxTT53/x3Uq1zIL45DoYKVfHH4CZqwgZhPg5qYROl5nQjV+85ruFzFGZxu+QeFVbRTPDRnqL9UbU4VeA=="],
"tree-sitter-bash": ["tree-sitter-bash@0.25.0", "", { "dependencies": { "node-addon-api": "^8.2.1", "node-gyp-build": "^4.8.2" }, "peerDependencies": { "tree-sitter": "^0.25.0" }, "optionalPeers": ["tree-sitter"] }, "sha512-gZtlj9+qFS81qKxpLfD6H0UssQ3QBc/F0nKkPsiFDyfQF2YBqYvglFJUzchrPpVhZe9kLZTrJ9n2J6lmka69Vg=="],
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
@ -3722,6 +3714,8 @@
"@opencode-ai/web/shiki": ["shiki@3.4.2", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/engine-javascript": "3.4.2", "@shikijs/engine-oniguruma": "3.4.2", "@shikijs/langs": "3.4.2", "@shikijs/themes": "3.4.2", "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-wuxzZzQG8kvZndD7nustrNFIKYJ1jJoWIPaBpVe2+KHSvtzMi4SBjOxrigs8qeqce/l3U0cwiC+VAkLKSunHQQ=="],
"@opentui/core/web-tree-sitter": ["web-tree-sitter@0.26.0", "", {}, "sha512-wGGAMnJEMF8wy33iEGxSvnyEOfVLzSaa3x6g66aEHsL/hsgFb6IVPrpacIordAMz198pE9qReCEqFUuM0pnfwg=="],
"@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
"@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.9", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.1" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.8" }, "optionalPeers": ["solid-js"] }, "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw=="],
@ -4072,10 +4066,6 @@
"tree-sitter-bash/node-addon-api": ["node-addon-api@8.5.0", "", {}, "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A=="],
"tree-sitter-javascript/node-addon-api": ["node-addon-api@8.5.0", "", {}, "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A=="],
"tree-sitter-typescript/node-addon-api": ["node-addon-api@8.5.0", "", {}, "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A=="],
"tw-to-css/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
"tw-to-css/tailwindcss": ["tailwindcss@3.3.2", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.12", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.18.2", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", "postcss": "^8.4.23", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w=="],

View file

@ -77,10 +77,8 @@
"partial-json": "0.1.7",
"remeda": "catalog:",
"solid-js": "catalog:",
"tree-sitter": "0.22.4",
"tree-sitter-bash": "0.23.3",
"tree-sitter-highlight": "1.0.1",
"tree-sitter-typescript": "0.23.2",
"tree-sitter": "0.25.0",
"tree-sitter-bash": "0.25.0",
"turndown": "7.2.0",
"ulid": "catalog:",
"vscode-jsonrpc": "8.2.1",

View file

@ -28,8 +28,5 @@ export const TuiEvent = {
]),
}),
),
ToastShow: Bus.event(
"tui.toast.show",
ToastSchema,
),
ToastShow: Bus.event("tui.toast.show", ToastSchema),
}

View file

@ -1,6 +1,12 @@
import { Log } from "../util/log"
import { Bus } from "../bus"
import { describeRoute, generateSpecs, validator, resolver, openAPIRouteHandler } from "hono-openapi"
import {
describeRoute,
generateSpecs,
validator,
resolver,
openAPIRouteHandler,
} from "hono-openapi"
import { Hono } from "hono"
import { cors } from "hono/cors"
import { stream, streamSSE } from "hono/streaming"
@ -35,7 +41,7 @@ import { InstanceBootstrap } from "../project/bootstrap"
import { MCP } from "../mcp"
import { Storage } from "../storage/storage"
import type { ContentfulStatusCode } from "hono/utils/http-status"
import { TuiEvent } from "@/cli/cmd/tui/event"
import { type TuiEvent } from "@/cli/cmd/tui/event"
import { Snapshot } from "@/snapshot"
import { SessionSummary } from "@/session/summary"
@ -249,7 +255,9 @@ export namespace Server {
id: t.id,
description: t.description,
// Handle both Zod schemas and plain JSON schemas
parameters: (t.parameters as any)?._def ? zodToJsonSchema(t.parameters as any) : t.parameters,
parameters: (t.parameters as any)?._def
? zodToJsonSchema(t.parameters as any)
: t.parameters,
})),
)
},
@ -1038,7 +1046,10 @@ export namespace Server {
const providers = await Provider.list().then((x) => mapValues(x, (item) => item.info))
return c.json({
providers: Object.values(providers),
default: mapValues(providers, (item) => Provider.sort(Object.values(item.models))[0].id),
default: mapValues(
providers,
(item) => Provider.sort(Object.values(item.models))[0].id,
),
})
},
)
@ -1579,7 +1590,10 @@ export namespace Server {
),
async (c) => {
const evt = c.req.valid("json")
await Bus.publish(Object.values(TuiEvent).find((def) => def.type === evt.type)!, evt.properties)
await Bus.publish(
Object.values(TuiEvent).find((def) => def.type === evt.type)!,
evt.properties,
)
return c.json(true)
},
)

View file

@ -56,7 +56,8 @@ export namespace Snapshot {
.trim()
.split("\n")
.map((x) => x.trim())
.filter(Boolean),
.filter(Boolean)
.map((x) => path.join(Instance.worktree, x)),
}
}
@ -101,8 +102,7 @@ export namespace Snapshot {
})
} else {
log.info("file did not exist in snapshot, deleting", { file })
const absolutePath = path.join(Instance.worktree, file)
await fs.unlink(absolutePath).catch(() => {})
await fs.unlink(file).catch(() => {})
}
}
files.add(file)

View file

@ -4,6 +4,13 @@ import { Tool } from "./tool"
import DESCRIPTION from "./bash.txt"
import { Log } from "../util/log"
import { Instance } from "../project/instance"
import { lazy } from "@/util/lazy"
import { Language } from "web-tree-sitter"
import { Agent } from "@/agent/agent"
import { $ } from "bun"
import { Filesystem } from "@/util/filesystem"
import { Wildcard } from "@/util/wildcard"
import { Permission } from "@/permission"
const MAX_OUTPUT_LENGTH = 30_000
const DEFAULT_TIMEOUT = 1 * 60 * 1000
@ -12,34 +19,24 @@ const SIGKILL_TIMEOUT_MS = 200
export const log = Log.create({ service: "bash-tool" })
/*
const parser = lazy(async () => {
try {
const { default: Parser } = await import("tree-sitter")
const Bash = await import("tree-sitter-bash")
const p = new Parser()
p.setLanguage(Bash.language as any)
return p
} catch (e) {
const { default: Parser } = await import("web-tree-sitter")
const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, {
with: { type: "wasm" },
})
await Parser.init({
locateFile() {
return treeWasm
},
})
const { default: bashWasm } = await import("tree-sitter-bash/tree-sitter-bash.wasm" as string, {
with: { type: "wasm" },
})
const bashLanguage = await Language.load(bashWasm)
const p = new Parser()
p.setLanguage(bashLanguage)
return p
}
const { Parser } = await import("web-tree-sitter")
const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, {
with: { type: "wasm" },
})
await Parser.init({
locateFile() {
return treeWasm
},
})
const { default: bashWasm } = await import("tree-sitter-bash/tree-sitter-bash.wasm" as string, {
with: { type: "wasm" },
})
const bashLanguage = await Language.load(bashWasm)
const p = new Parser()
p.setLanguage(bashLanguage)
return p
})
*/
export const BashTool = Tool.define("bash", {
description: DESCRIPTION,
@ -59,7 +56,6 @@ export const BashTool = Tool.define("bash", {
)
}
const timeout = Math.min(params.timeout ?? DEFAULT_TIMEOUT, MAX_TIMEOUT)
/*
const tree = await parser().then((p) => p.parse(params.command))
if (!tree) {
throw new Error("Failed to parse command")
@ -143,7 +139,6 @@ export const BashTool = Tool.define("bash", {
},
})
}
*/
const proc = spawn(params.command, {
shell: true,

View file

@ -126,7 +126,7 @@ test("binary file handling", async () => {
await Bun.write(`${tmp.path}/image.png`, new Uint8Array([0x89, 0x50, 0x4e, 0x47]))
const patch = await Snapshot.patch(before!)
expect(patch.files).toContain(`image.png`)
expect(patch.files).toContain(`${tmp.path}/image.png`)
await Snapshot.revert([patch])
expect(await Bun.file(`${tmp.path}/image.png`).exists()).toBe(false)
@ -195,9 +195,9 @@ test("special characters in filenames", async () => {
await Bun.write(`${tmp.path}/file_with_underscores.txt`, "UNDERSCORES")
const files = (await Snapshot.patch(before!)).files
expect(files).toContain(`file with spaces.txt`)
expect(files).toContain(`file-with-dashes.txt`)
expect(files).toContain(`file_with_underscores.txt`)
expect(files).toContain(`${tmp.path}/file with spaces.txt`)
expect(files).toContain(`${tmp.path}/file-with-dashes.txt`)
expect(files).toContain(`${tmp.path}/file_with_underscores.txt`)
},
})
})
@ -249,7 +249,7 @@ test("revert non-existent file", async () => {
Snapshot.revert([
{
hash: before!,
files: [`nonexistent.txt`],
files: [`${tmp.path}/nonexistent.txt`],
},
]),
).resolves.toBeUndefined()
@ -301,7 +301,7 @@ test("very long filenames", async () => {
await Bun.write(longFile, "long filename content")
const patch = await Snapshot.patch(before!)
expect(patch.files).toContain(longName)
expect(patch.files).toContain(longFile)
await Snapshot.revert([patch])
expect(await Bun.file(longFile).exists()).toBe(false)
@ -322,9 +322,9 @@ test("hidden files", async () => {
await Bun.write(`${tmp.path}/.config`, "config content")
const patch = await Snapshot.patch(before!)
expect(patch.files).toContain(`.hidden`)
expect(patch.files).toContain(`.gitignore`)
expect(patch.files).toContain(`.config`)
expect(patch.files).toContain(`${tmp.path}/.hidden`)
expect(patch.files).toContain(`${tmp.path}/.gitignore`)
expect(patch.files).toContain(`${tmp.path}/.config`)
},
})
})
@ -343,8 +343,8 @@ test("nested symlinks", async () => {
await $`ln -s ${tmp.path}/sub ${tmp.path}/sub-link`.quiet()
const patch = await Snapshot.patch(before!)
expect(patch.files).toContain(`sub/dir/link.txt`)
expect(patch.files).toContain(`sub-link`)
expect(patch.files).toContain(`${tmp.path}/sub/dir/link.txt`)
expect(patch.files).toContain(`${tmp.path}/sub-link`)
},
})
})
@ -402,11 +402,11 @@ test("gitignore changes", async () => {
const patch = await Snapshot.patch(before!)
// Should track gitignore itself
expect(patch.files).toContain(`.gitignore`)
expect(patch.files).toContain(`${tmp.path}/.gitignore`)
// Should track normal files
expect(patch.files).toContain(`normal.txt`)
expect(patch.files).toContain(`${tmp.path}/normal.txt`)
// Should not track ignored files (git won't see them)
expect(patch.files).not.toContain(`test.ignored`)
expect(patch.files).not.toContain(`${tmp.path}/test.ignored`)
},
})
})
@ -451,7 +451,7 @@ test("snapshot state isolation between projects", async () => {
const before1 = await Snapshot.track()
await Bun.write(`${tmp1.path}/project1.txt`, "project1 content")
const patch1 = await Snapshot.patch(before1!)
expect(patch1.files).toContain(`project1.txt`)
expect(patch1.files).toContain(`${tmp1.path}/project1.txt`)
},
})
@ -461,10 +461,10 @@ test("snapshot state isolation between projects", async () => {
const before2 = await Snapshot.track()
await Bun.write(`${tmp2.path}/project2.txt`, "project2 content")
const patch2 = await Snapshot.patch(before2!)
expect(patch2.files).toContain(`project2.txt`)
expect(patch2.files).toContain(`${tmp2.path}/project2.txt`)
// Ensure project1 files don't appear in project2
expect(patch2.files).not.toContain(`project1.txt`)
expect(patch2.files).not.toContain(`${tmp1?.path}/project1.txt`)
},
})
})
@ -549,7 +549,7 @@ test("revert should not delete files that existed but were deleted in snapshot",
await Bun.write(`${tmp.path}/a.txt`, "recreated content")
const patch = await Snapshot.patch(snapshot2!)
expect(patch.files).toContain(`a.txt`)
expect(patch.files).toContain(`${tmp.path}/a.txt`)
await Snapshot.revert([patch])
@ -573,8 +573,8 @@ test("revert preserves file that existed in snapshot when deleted then recreated
await Bun.write(`${tmp.path}/newfile.txt`, "new")
const patch = await Snapshot.patch(snapshot!)
expect(patch.files).toContain(`existing.txt`)
expect(patch.files).toContain(`newfile.txt`)
expect(patch.files).toContain(`${tmp.path}/existing.txt`)
expect(patch.files).toContain(`${tmp.path}/newfile.txt`)
await Snapshot.revert([patch])

View file

@ -21,7 +21,9 @@ describe("tool.patch", () => {
await Instance.provide({
directory: "/tmp",
fn: async () => {
await expect(patchTool.execute({ patchText: "" }, ctx)).rejects.toThrow("patchText is required")
await expect(patchTool.execute({ patchText: "" }, ctx)).rejects.toThrow(
"patchText is required",
)
},
})
})
@ -30,7 +32,9 @@ describe("tool.patch", () => {
await Instance.provide({
directory: "/tmp",
fn: async () => {
await expect(patchTool.execute({ patchText: "invalid patch" }, ctx)).rejects.toThrow("Failed to parse patch")
await expect(patchTool.execute({ patchText: "invalid patch" }, ctx)).rejects.toThrow(
"Failed to parse patch",
)
},
})
})
@ -113,7 +117,9 @@ describe("tool.patch", () => {
// Verify file was created with correct content
const filePath = path.join(fixture.path, "config.js")
const content = await fs.readFile(filePath, "utf-8")
expect(content).toBe('const API_KEY = "test-key"\nconst DEBUG = false\nconst VERSION = "1.0"')
expect(content).toBe(
'const API_KEY = "test-key"\nconst DEBUG = false\nconst VERSION = "1.0"',
)
},
})
})