Skip to content
This repository was archived by the owner on Feb 25, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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