From 812c9814f09d3ae891cb4a459ed542aa36e39ffa Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 29 Mar 2026 04:40:10 +0800 Subject: [PATCH 1/2] fix: normalize windows path separators in session queries Fixes Windows session list display issues where sessions become invisible due to path separator inconsistencies. Changes: - Add normalizePathSeparators() function to filesystem utilities - Normalize paths in session.createNext() when storing sessions - Normalize paths in Session.list() when querying by directory - Normalize paths in Session.listGlobal() when querying by directory - Normalize paths in Project.discover() when updating session project IDs This resolves GitHub Issues #16744 and #18029 where sessions created with backslash paths would not be visible when querying with forward slash paths, or vice versa. Related to PR #17067 by HRronaldo Co-Authored-By: Claude Haiku 4.5 --- packages/opencode/src/project/project.ts | 3 ++- packages/opencode/src/session/index.ts | 7 ++++--- packages/opencode/src/util/filesystem.ts | 9 +++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts index f4b8b940d20f..f36881d4a3a0 100644 --- a/packages/opencode/src/project/project.ts +++ b/packages/opencode/src/project/project.ts @@ -14,6 +14,7 @@ import { NodeFileSystem, NodePath } from "@effect/platform-node" import { makeRuntime } from "@/effect/run-service" import { AppFileSystem } from "@/filesystem" import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner" +import { Filesystem } from "@/util/filesystem" export namespace Project { const log = Log.create({ service: "project" }) @@ -315,7 +316,7 @@ export namespace Project { d .update(SessionTable) .set({ project_id: data.id }) - .where(and(eq(SessionTable.project_id, ProjectID.global), eq(SessionTable.directory, data.worktree))) + .where(and(eq(SessionTable.project_id, ProjectID.global), eq(SessionTable.directory, Filesystem.normalizePathSeparators(data.worktree)))) .run(), ) } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index eb01739c156f..5deeb84a4608 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -17,6 +17,7 @@ import { ProjectTable } from "../project/project.sql" import { Storage } from "@/storage/storage" import { Log } from "../util/log" import { updateSchema } from "../util/update-schema" +import { Filesystem } from "@/util/filesystem" import { MessageV2 } from "./message-v2" import { Instance } from "../project/instance" import { SessionPrompt } from "./prompt" @@ -400,7 +401,7 @@ export namespace Session { slug: Slug.create(), version: Installation.VERSION, projectID: Instance.project.id, - directory: input.directory, + directory: Filesystem.normalizePathSeparators(input.directory), workspaceID: input.workspaceID, parentID: input.parentID, title: input.title ?? createDefaultTitle(!!input.parentID), @@ -768,7 +769,7 @@ export namespace Session { conditions.push(eq(SessionTable.workspace_id, input.workspaceID)) } if (input?.directory) { - conditions.push(eq(SessionTable.directory, input.directory)) + conditions.push(eq(SessionTable.directory, Filesystem.normalizePathSeparators(input.directory))) } if (input?.roots) { conditions.push(isNull(SessionTable.parent_id)) @@ -808,7 +809,7 @@ export namespace Session { const conditions: SQL[] = [] if (input?.directory) { - conditions.push(eq(SessionTable.directory, input.directory)) + conditions.push(eq(SessionTable.directory, Filesystem.normalizePathSeparators(input.directory))) } if (input?.roots) { conditions.push(isNull(SessionTable.parent_id)) diff --git a/packages/opencode/src/util/filesystem.ts b/packages/opencode/src/util/filesystem.ts index 37f00c6b9c8e..4564b8bcb749 100644 --- a/packages/opencode/src/util/filesystem.ts +++ b/packages/opencode/src/util/filesystem.ts @@ -113,6 +113,15 @@ export namespace Filesystem { } } + /** + * Normalize path separators to forward slashes for consistent database queries. + * This fixes Windows path compatibility issues where paths might use backslashes + * in some cases and forward slashes in others. + */ + export function normalizePathSeparators(p: string): string { + return p.replace(/\\/g, "/") + } + // We cannot rely on path.resolve() here because git.exe may come from Git Bash, Cygwin, or MSYS2, so we need to translate these paths at the boundary. // Also resolves symlinks so that callers using the result as a cache key // always get the same canonical path for a given physical directory. From c8f3897ba63656ff048f307d0a23032ba5578bcd Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 29 Mar 2026 15:56:06 +0800 Subject: [PATCH 2/2] fix: add windows drive letter case normalization Also normalize Windows drive letters to uppercase for consistent path matching. This ensures paths like 'c:/path' and 'C:/path' are treated the same. --- packages/opencode/src/util/filesystem.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/util/filesystem.ts b/packages/opencode/src/util/filesystem.ts index 4564b8bcb749..fdccb5b498b2 100644 --- a/packages/opencode/src/util/filesystem.ts +++ b/packages/opencode/src/util/filesystem.ts @@ -116,10 +116,18 @@ export namespace Filesystem { /** * Normalize path separators to forward slashes for consistent database queries. * This fixes Windows path compatibility issues where paths might use backslashes - * in some cases and forward slashes in others. + * in some cases and forward slashes in others, and ensures consistent drive letter casing. */ export function normalizePathSeparators(p: string): string { - return p.replace(/\\/g, "/") + // Convert backslashes to forward slashes + let normalized = p.replace(/\\/g, "/") + + // Normalize Windows drive letters to uppercase (e.g., c:/ -> C:/) + if (/^[a-z]:\//.test(normalized)) { + normalized = normalized[0].toUpperCase() + normalized.slice(1) + } + + return normalized } // We cannot rely on path.resolve() here because git.exe may come from Git Bash, Cygwin, or MSYS2, so we need to translate these paths at the boundary.