Skip to content
This repository was archived by the owner on Feb 25, 2026. It is now read-only.
1 change: 0 additions & 1 deletion packages/app/src/custom-elements.d.ts

This file was deleted.

18 changes: 18 additions & 0 deletions packages/app/src/custom-elements.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// kilocode_change - copy content instead of symlink for Windows compatibility
import { DIFFS_TAG_NAME } from "@pierre/diffs"

/**
* TypeScript declaration for the <diffs-container> custom element.
* This tells TypeScript that <diffs-container> is a valid JSX element in SolidJS.
* Required for using the @pierre/diffs web component in .tsx files.
*/

declare module "solid-js" {
namespace JSX {
interface IntrinsicElements {
[DIFFS_TAG_NAME]: HTMLAttributes<HTMLElement>
}
}
}

export {}
7 changes: 4 additions & 3 deletions packages/opencode/src/agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Plugin } from "@/plugin"
import { Skill } from "../skill"

import { Telemetry } from "@kilocode/kilo-telemetry" // kilocode_change
import { Filesystem } from "@/util/filesystem"

export namespace Agent {
export const Info = z
Expand Down Expand Up @@ -106,12 +107,12 @@ export namespace Agent {
question: "allow",
plan_exit: "allow",
external_directory: {
[path.join(Global.Path.data, "plans", "*")]: "allow",
[Filesystem.join(Global.Path.data, "plans", "*")]: "allow",
},
edit: {
"*": "deny",
[path.join(".opencode", "plans", "*.md")]: "allow",
[path.relative(Instance.worktree, path.join(Global.Path.data, path.join("plans", "*.md")))]: "allow",
[Filesystem.join(".opencode", "plans", "*.md")]: "allow",
[Filesystem.relative(Instance.worktree, Filesystem.join(Global.Path.data, "plans", "*.md"))]: "allow",
},
}),
user,
Expand Down
10 changes: 5 additions & 5 deletions packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ export function Session() {
} else {
const exportDir = process.cwd()
const filename = options.filename.trim()
const filepath = path.join(exportDir, filename)
const filepath = Filesystem.join(exportDir, filename)

await Bun.write(filepath, transcript)

Expand Down Expand Up @@ -1675,7 +1675,7 @@ function Bash(props: ToolProps<typeof BashTool>) {
const base = sync.data.path.directory
if (!base) return undefined

const absolute = path.resolve(base, workdir)
const absolute = Filesystem.resolve(base, workdir)
if (absolute === base) return undefined

const home = Global.Path.home
Expand Down Expand Up @@ -1730,7 +1730,7 @@ function Write(props: ToolProps<typeof WriteTool>) {
})

const diagnostics = createMemo(() => {
const filePath = Filesystem.normalizePath(props.input.filePath ?? "")
const filePath = Filesystem.realpath(props.input.filePath ?? "")
return props.metadata.diagnostics?.[filePath] ?? []
})

Expand Down Expand Up @@ -1940,7 +1940,7 @@ function Edit(props: ToolProps<typeof EditTool>) {
const diffContent = createMemo(() => props.metadata.diff)

const diagnostics = createMemo(() => {
const filePath = Filesystem.normalizePath(props.input.filePath ?? "")
const filePath = Filesystem.realpath(props.input.filePath ?? "")
const arr = props.metadata.diagnostics?.[filePath] ?? []
return arr.filter((x) => x.severity === 1).slice(0, 3)
})
Expand Down Expand Up @@ -2133,7 +2133,7 @@ function Skill(props: ToolProps<typeof SkillTool>) {
function normalizePath(input?: string) {
if (!input) return ""
if (path.isAbsolute(input)) {
return path.relative(process.cwd(), input) || "."
return Filesystem.relative(process.cwd(), input) || "."
}
return input
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Keybind } from "@/util/keybind"
import { Locale } from "@/util/locale"
import { Global } from "@/global"
import { useDialog } from "../../ui/dialog"
import { Filesystem } from "@/util/filesystem"

type PermissionStage = "permission" | "always" | "reject"

Expand All @@ -23,14 +24,14 @@ function normalizePath(input?: string) {

const cwd = process.cwd()
const home = Global.Path.home
const absolute = path.isAbsolute(input) ? input : path.resolve(cwd, input)
const relative = path.relative(cwd, absolute)
const absolute = Filesystem.normalize(path.isAbsolute(input) ? input : Filesystem.resolve(cwd, input))
const relative = Filesystem.relative(cwd, absolute)

if (!relative) return "."
if (!relative.startsWith("..")) return relative

// outside cwd - use ~ or absolute
if (home && (absolute === home || absolute.startsWith(home + path.sep))) {
if (home && (absolute === home || absolute.startsWith(home + '/'))) {
return absolute.replace(home, "~")
}
return absolute
Expand Down Expand Up @@ -248,7 +249,7 @@ export function PermissionPrompt(props: { request: PermissionRequest }) {
const derived =
typeof pattern === "string"
? pattern.includes("*")
? path.dirname(pattern)
? Filesystem.dirname(pattern)
: pattern
: undefined

Expand Down
7 changes: 5 additions & 2 deletions packages/opencode/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,10 +411,13 @@ export namespace Config {
}

function rel(item: string, patterns: string[]) {
// Normalize the item path first
const normalizedItem = Filesystem.normalize(item)
for (const pattern of patterns) {
const index = item.indexOf(pattern)
const normalizedPattern = Filesystem.normalize(pattern)
const index = normalizedItem.indexOf(normalizedPattern)
if (index === -1) continue
return item.slice(index + pattern.length)
return normalizedItem.slice(index + normalizedPattern.length)
}
}

Expand Down
10 changes: 6 additions & 4 deletions packages/opencode/src/file/ignore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { sep } from "node:path"
import { Filesystem } from "../util/filesystem"

export namespace FileIgnore {
const FOLDERS = new Set([
Expand Down Expand Up @@ -64,18 +64,20 @@ export namespace FileIgnore {
whitelist?: Bun.Glob[]
},
) {
const normalizedPath = Filesystem.normalize(filepath)

for (const glob of opts?.whitelist || []) {
if (glob.match(filepath)) return false
if (glob.match(normalizedPath)) return false
}

const parts = filepath.split(sep)
const parts = normalizedPath.split("/")
for (let i = 0; i < parts.length; i++) {
if (FOLDERS.has(parts[i])) return true
}

const extra = opts?.extra || []
for (const glob of [...FILE_GLOBS, ...extra]) {
if (glob.match(filepath)) return true
if (glob.match(normalizedPath)) return true
}

return false
Expand Down
6 changes: 3 additions & 3 deletions packages/opencode/src/kilocode/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export namespace KilocodePaths {
}),
)
for (const dir of projectDirs) {
const skillsDir = path.join(dir, "skills")
const skillsDir = Filesystem.join(dir, "skills")
if (await Filesystem.isDir(skillsDir)) {
directories.push(dir) // Return parent (.kilocode/), not skills/
}
Expand All @@ -71,14 +71,14 @@ export namespace KilocodePaths {
if (!opts.skipGlobalPaths) {
// 2. Global ~/.kilocode/
const global = globalDir()
const globalSkills = path.join(global, "skills")
const globalSkills = Filesystem.join(global, "skills")
if (await Filesystem.isDir(globalSkills)) {
directories.push(global) // Return parent, not skills/
}

// 3. VSCode extension global storage (marketplace-installed skills)
const vscode = vscodeGlobalStorage()
const vscodeSkills = path.join(vscode, "skills")
const vscodeSkills = Filesystem.join(vscode, "skills")
if (await Filesystem.isDir(vscodeSkills)) {
directories.push(vscode) // Return parent, not skills/
}
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/lsp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export namespace LSPClient {

const diagnostics = new Map<string, Diagnostic[]>()
connection.onNotification("textDocument/publishDiagnostics", (params) => {
const filePath = Filesystem.normalizePath(fileURLToPath(params.uri))
const filePath = Filesystem.realpath(fileURLToPath(params.uri))
l.info("textDocument/publishDiagnostics", {
path: filePath,
count: params.diagnostics.length,
Expand Down Expand Up @@ -208,7 +208,7 @@ export namespace LSPClient {
return diagnostics
},
async waitForDiagnostics(input: { path: string }) {
const normalizedPath = Filesystem.normalizePath(
const normalizedPath = Filesystem.realpath(
path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path),
)
log.info("waiting for diagnostics", { path: normalizedPath })
Expand Down
4 changes: 3 additions & 1 deletion packages/opencode/src/lsp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Config } from "../config/config"
import { spawn } from "child_process"
import { Instance } from "../project/instance"
import { Flag } from "@/flag/flag"
import { Filesystem } from "@/util/filesystem"

export namespace LSP {
const log = Log.create({ service: "lsp" })
Expand Down Expand Up @@ -111,9 +112,10 @@ export namespace LSP {
root: existing?.root ?? (async () => Instance.directory),
extensions: item.extensions ?? existing?.extensions ?? [],
spawn: async (root) => {
const normalizedRoot = Filesystem.normalize(root)
return {
process: spawn(item.command[0], item.command.slice(1), {
cwd: root,
cwd: normalizedRoot,
env: {
...process.env,
...item.env,
Expand Down
8 changes: 4 additions & 4 deletions packages/opencode/src/patch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,23 @@ export namespace Patch {
const line = lines[startIdx]

if (line.startsWith("*** Add File:")) {
const filePath = line.split(":", 2)[1]?.trim()
const filePath = line.substring("*** Add File:".length).trim()
return filePath ? { filePath, nextIdx: startIdx + 1 } : null
}

if (line.startsWith("*** Delete File:")) {
const filePath = line.split(":", 2)[1]?.trim()
const filePath = line.substring("*** Delete File:".length).trim()
return filePath ? { filePath, nextIdx: startIdx + 1 } : null
}

if (line.startsWith("*** Update File:")) {
const filePath = line.split(":", 2)[1]?.trim()
const filePath = line.substring("*** Update File:".length).trim()
let movePath: string | undefined
let nextIdx = startIdx + 1

// Check for move directive
if (nextIdx < lines.length && lines[nextIdx].startsWith("*** Move to:")) {
movePath = lines[nextIdx].split(":", 2)[1]?.trim()
movePath = lines[nextIdx].substring("*** Move to:".length).trim()
nextIdx++
}

Expand Down
11 changes: 6 additions & 5 deletions packages/opencode/src/project/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ const disposal = {

export const Instance = {
async provide<R>(input: { directory: string; init?: () => Promise<any>; fn: () => R }): Promise<R> {
let existing = cache.get(input.directory)
const directory = Filesystem.normalize(input.directory)
let existing = cache.get(directory)
if (!existing) {
Log.Default.info("creating instance", { directory: input.directory })
Log.Default.info("creating instance", { directory })
existing = iife(async () => {
const { project, sandbox } = await Project.fromDirectory(input.directory)
const { project, sandbox } = await Project.fromDirectory(directory)
const ctx = {
directory: input.directory,
directory: directory,
worktree: sandbox,
project,
}
Expand All @@ -35,7 +36,7 @@ export const Instance = {
})
return ctx
})
cache.set(input.directory, existing)
cache.set(directory, existing)
}
const ctx = await existing
return context.provide(ctx, async () => {
Expand Down
10 changes: 5 additions & 5 deletions packages/opencode/src/project/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ export namespace Project {
const dotgit = await matches.next().then((x) => x.value)
await matches.return()
if (dotgit) {
let sandbox = path.dirname(dotgit)
let sandbox = Filesystem.dirname(dotgit)

const gitBinary = Bun.which("git")

// cached id calculation
let id = await Bun.file(path.join(dotgit, "opencode"))
let id = await Bun.file(Filesystem.join(dotgit, "opencode"))
.text()
.then((x) => x.trim())
.catch(() => undefined)
Expand Down Expand Up @@ -102,7 +102,7 @@ export namespace Project {

id = roots[0]
if (id) {
void Bun.file(path.join(dotgit, "opencode"))
void Bun.file(Filesystem.join(dotgit, "opencode"))
.write(id)
.catch(() => undefined)
}
Expand All @@ -120,7 +120,7 @@ export namespace Project {
const top = await git(["rev-parse", "--show-toplevel"], {
cwd: sandbox,
})
.then(async (result) => path.resolve(sandbox, (await result.text()).trim()))
.then(async (result) => Filesystem.resolve(sandbox, (await result.text()).trim()))
.catch(() => undefined)

if (!top) {
Expand All @@ -138,7 +138,7 @@ export namespace Project {
cwd: sandbox,
})
.then(async (result) => {
const dirname = path.dirname((await result.text()).trim())
const dirname = Filesystem.dirname((await result.text()).trim())
if (dirname === ".") return sandbox
return dirname
})
Expand Down
9 changes: 8 additions & 1 deletion packages/opencode/src/pty/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Log } from "../util/log"
import { Instance } from "../project/instance"
import { lazy } from "@opencode-ai/util/lazy"
import { Shell } from "@/shell/shell"
import { Filesystem } from "@/util/filesystem"
import { Plugin } from "@/plugin"

export namespace Pty {
Expand Down Expand Up @@ -134,7 +135,13 @@ export namespace Pty {
args.push("-l")
}

const cwd = input.cwd || Instance.directory
const cwd = Filesystem.normalize(input.cwd || Instance.directory)

// Validate directory exists
if (!(await Filesystem.isDir(cwd))) {
throw new Error(`Directory does not exist: ${cwd}`)
}

const shellEnv = await Plugin.trigger("shell.env", { cwd }, { env: {} })
const env = {
...process.env,
Expand Down
6 changes: 5 additions & 1 deletion packages/opencode/src/server/routes/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Log } from "../../util/log"
import { PermissionNext } from "@/permission/next"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { Filesystem } from "@/util/filesystem"

const log = Log.create({ service: "server" })

Expand Down Expand Up @@ -54,9 +55,12 @@ export const SessionRoutes = lazy(() =>
async (c) => {
const query = c.req.valid("query")
const term = query.search?.toLowerCase()
// Normalize directory path for comparison
const normalizedQueryDir = query.directory ? Filesystem.normalize(query.directory) : undefined
const sessions: Session.Info[] = []
for await (const session of Session.list()) {
if (query.directory !== undefined && session.directory !== query.directory) continue
if (normalizedQueryDir !== undefined && Filesystem.normalize(session.directory) !== normalizedQueryDir)
continue
if (query.roots && session.parentID) continue
if (query.start !== undefined && session.time.updated < query.start) continue
if (term !== undefined && !session.title.toLowerCase().includes(term)) continue
Expand Down
Loading
Loading