From fa89273b4354ad53b4a6149a0cbedeef747dd725 Mon Sep 17 00:00:00 2001 From: Samir Alibabic Date: Thu, 23 Jul 2026 18:03:03 +0200 Subject: [PATCH] fix(session): scope default lists by directory --- packages/opencode/src/cli/cmd/session.ts | 15 ++++++++++++- packages/opencode/test/cli/session.test.ts | 12 ++++++++++ .../tui/src/component/dialog-session-list.tsx | 2 +- packages/tui/src/context/sync.tsx | 22 +++++++++++-------- .../component/dialog-session-list.test.ts | 6 ++--- .../test/context/session-list-filter.test.ts | 16 ++++++++++++++ 6 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 packages/opencode/test/cli/session.test.ts create mode 100644 packages/tui/test/context/session-list-filter.test.ts diff --git a/packages/opencode/src/cli/cmd/session.ts b/packages/opencode/src/cli/cmd/session.ts index 9e6ddda9d2d8..84644884ebdd 100644 --- a/packages/opencode/src/cli/cmd/session.ts +++ b/packages/opencode/src/cli/cmd/session.ts @@ -13,6 +13,7 @@ import { NotFoundError } from "@/storage/storage" import { EOL } from "os" import path from "path" import { which } from "@opencode-ai/core/util/which" +import { InstanceRef } from "@/effect/instance-ref" function pagerCmd(): string[] { const lessOptions = ["-R", "-S"] @@ -84,7 +85,11 @@ export const SessionListCommand = effectCmd({ default: "table", }), handler: Effect.fn("Cli.session.list")(function* (args) { - const sessions = yield* Session.Service.use((svc) => svc.list({ roots: true, limit: args.maxCount })) + const ctx = yield* InstanceRef + if (!ctx) return yield* Effect.die("InstanceRef not provided") + const sessions = yield* Session.Service.use((svc) => + svc.list(createSessionListQuery({ directory: ctx.directory, maxCount: args.maxCount })), + ) if (sessions.length === 0) return @@ -115,6 +120,14 @@ export const SessionListCommand = effectCmd({ }), }) +export function createSessionListQuery(input: { directory: string; maxCount?: number }) { + return { + directory: input.directory, + roots: true, + limit: input.maxCount, + } +} + function formatSessionTable(sessions: Session.Info[]): string { const lines: string[] = [] diff --git a/packages/opencode/test/cli/session.test.ts b/packages/opencode/test/cli/session.test.ts new file mode 100644 index 000000000000..f78eb1859bbc --- /dev/null +++ b/packages/opencode/test/cli/session.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, test } from "bun:test" +import { createSessionListQuery } from "../../src/cli/cmd/session" + +describe("session list", () => { + test("scopes the query to the current exact directory", () => { + expect(createSessionListQuery({ directory: "/workspace/packages/opencode", maxCount: 12 })).toEqual({ + directory: "/workspace/packages/opencode", + roots: true, + limit: 12, + }) + }) +}) diff --git a/packages/tui/src/component/dialog-session-list.tsx b/packages/tui/src/component/dialog-session-list.tsx index c508fc6b7b01..5a78adf427d0 100644 --- a/packages/tui/src/component/dialog-session-list.tsx +++ b/packages/tui/src/component/dialog-session-list.tsx @@ -19,7 +19,7 @@ import { DialogSessionDeleteFailed } from "./dialog-session-delete-failed" import { useCommandShortcut } from "../keymap" import { useEvent } from "../context/event" -type SessionListFilter = { scope?: "project"; path?: string } +type SessionListFilter = { scope?: "project"; directory?: string } export function createDialogSessionListQuery(input: { search?: string; filter: SessionListFilter }) { const search = input.search?.trim() diff --git a/packages/tui/src/context/sync.tsx b/packages/tui/src/context/sync.tsx index d0511c5183e4..50a57687dbb3 100644 --- a/packages/tui/src/context/sync.tsx +++ b/packages/tui/src/context/sync.tsx @@ -29,7 +29,6 @@ import { createSimpleContext } from "./helper" import { useExit } from "./exit" import { useArgs } from "./args" import { batch, onMount } from "solid-js" -import path from "path" import { useKV } from "./kv" import { usePermission } from "./permission" @@ -51,6 +50,14 @@ function search(items: T[], target: string, key: (item: T) => string) { return { found: false, index: left } } +export function createSessionListFilter(input: { directoryFilterEnabled: boolean; directory?: string }): { + scope?: "project" + directory?: string +} { + if (!input.directoryFilterEnabled || !input.directory) return { scope: "project" } + return { directory: input.directory } +} + export const { context: SyncContext, use: useSync, @@ -151,14 +158,11 @@ export const { hydratingSessions.get(sessionID)?.parts.add(partID) } - function sessionListQuery(): { scope?: "project"; path?: string } { - if (!kv.get("session_directory_filter_enabled", true)) return { scope: "project" } - if (!project.data.instance.path.worktree || !project.data.instance.path.directory) return { scope: "project" } - return { - path: path - .relative(path.resolve(project.data.instance.path.worktree), project.data.instance.path.directory) - .replaceAll("\\", "/"), - } + function sessionListQuery() { + return createSessionListFilter({ + directoryFilterEnabled: kv.get("session_directory_filter_enabled", true), + directory: project.data.instance.path.directory, + }) } function listSessions() { diff --git a/packages/tui/test/component/dialog-session-list.test.ts b/packages/tui/test/component/dialog-session-list.test.ts index 977904799504..8eab6d3619ce 100644 --- a/packages/tui/test/component/dialog-session-list.test.ts +++ b/packages/tui/test/component/dialog-session-list.test.ts @@ -2,11 +2,11 @@ import { describe, expect, test } from "bun:test" import { createDialogSessionListQuery, loadDialogSessionList } from "../../src/component/dialog-session-list" describe("dialog session list", () => { - test("requests root sessions for the default browse list", () => { - expect(createDialogSessionListQuery({ filter: { path: "packages/tui" } })).toEqual({ + test("requests root sessions in the exact directory for the default browse list", () => { + expect(createDialogSessionListQuery({ filter: { directory: "/workspace/packages/tui" } })).toEqual({ roots: true, limit: 100, - path: "packages/tui", + directory: "/workspace/packages/tui", }) }) diff --git a/packages/tui/test/context/session-list-filter.test.ts b/packages/tui/test/context/session-list-filter.test.ts new file mode 100644 index 000000000000..7eabc50e3be8 --- /dev/null +++ b/packages/tui/test/context/session-list-filter.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, test } from "bun:test" +import { createSessionListFilter } from "../../src/context/sync" + +describe("session list filter", () => { + test("uses the current exact directory when filtering is enabled", () => { + expect(createSessionListFilter({ directoryFilterEnabled: true, directory: "/workspace/packages/tui" })).toEqual({ + directory: "/workspace/packages/tui", + }) + }) + + test("uses the project scope when directory filtering is disabled", () => { + expect(createSessionListFilter({ directoryFilterEnabled: false, directory: "/workspace/packages/tui" })).toEqual({ + scope: "project", + }) + }) +})